decltype(auto)有哪些用途?

Nik*_*iou 133 c++ decltype auto return-type-deduction c++14

在c ++ 14 decltype(auto)中引入了成语.

通常,它的用途是允许auto声明使用decltype给定表达式的规则.

搜索成语的"好"用法示例我只能想到以下内容(由Scott Meyers提供),即函数的返回类型推导:

template<typename ContainerType, typename IndexType>                // C++14
decltype(auto) grab(ContainerType&& container, IndexType&& index)
{
  authenticateUser();
  return std::forward<ContainerType>(container)[std::forward<IndexType>(index)];
}
Run Code Online (Sandbox Code Playgroud)

这个新语言功能有用吗?

Tem*_*Rex 149

通用代码中的返回类型转发

对于非通用代码,如您给出的初始示例,您可以手动选择将引用作为返回类型:

auto const& Example(int const& i) 
{ 
    return i; 
}
Run Code Online (Sandbox Code Playgroud)

但是在通用代码中,您希望能够完全转发返回类型,而无需知道您是在处理引用还是值.decltype(auto)为您提供这种能力:

template<class Fun, class... Args>
decltype(auto) Example(Fun fun, Args&&... args) 
{ 
    return fun(std::forward<Args>(args)...); 
}
Run Code Online (Sandbox Code Playgroud)

在递归模板中延迟返回类型推导

在几天前的Q&A中,当模板的返回类型被指定为decltype(iter(Int<i-1>{}))而不是时,遇到模板实例化期间的无限递归decltype(auto).

template<int i> 
struct Int {};

constexpr auto iter(Int<0>) -> Int<0>;

template<int i>
constexpr auto iter(Int<i>) -> decltype(auto) 
{ return iter(Int<i-1>{}); }

int main() { decltype(iter(Int<10>{})) a; }
Run Code Online (Sandbox Code Playgroud)

decltype(auto)用于在模板实例化的尘埃落定后延迟返回类型推导.

其他用途

您也可以decltype(auto)在其他环境中使用,例如标准N3936草案也说明

7.1.6.4自动指定[dcl.spec.auto]

1 autodecltype(auto)类型指定器指定一个占位符类型,以后将被替换,可以通过从初始化程序中扣除,也可以通过使用尾随返回类型的显式指定来替换.该auto类型SPECI音响儿也用于表示一个lambda是一个通用的λ.

2 在这样的声明有效的任何上下文中,占位符类型可以在decl-specifier-seq,type-specifier-seq,conversion-function-id或trailing-return-type 与函数声明符一起出现.如果函数声明符包含trailing-return-type(8.3.5),则指定函数的声明返回类型.如果函数的声明返回类型包含占位符类型,则函数的返回类型将从函数体中的return语句推导出来(如果有).

草案还包含变量初始化的这个例子:

int i;
int&& f();
auto x3a = i;                  // decltype(x3a) is int
decltype(auto) x3d = i;        // decltype(x3d) is int
auto x4a = (i);                // decltype(x4a) is int
decltype(auto) x4d = (i);      // decltype(x4d) is int&
auto x5a = f();                // decltype(x5a) is int
decltype(auto) x5d = f();      // decltype(x5d) is int&&
auto x6a = { 1, 2 };           // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i;                // decltype(x7a) is int*
decltype(auto)*x7d = &i;       // error, declared type is not plain decltype(auto)
Run Code Online (Sandbox Code Playgroud)

  • `(i)`vs`i`的不同行为是否是C++ 14中的新东西? (16认同)
  • @Danvil`decltype(expr)`和`decltype((expr))`在C++ 11中已经不同了,这概括了这种行为. (14认同)
  • 我刚刚学会了这一点,感觉就像一个糟糕的设计决定......为括号的语法含义添加了一个准时的细微差别. (9认同)
  • @Bruice:“为什么在 decltype(auto) x4d = (i); decltype(x4d) 中是 int&amp; ?” Decltype 有两种性格。有(1)和(2):https://en.cppreference.com/w/cpp/language/decltype 它可以应用于名称或其他表达式。decltype(i) 表示“名为 `i` 的实体的类型”,而 decltype((i)) 会阻止该解释,并表示“计算结果为对 `i` 的引用的表达式的类型”。在我看来,这是非常微妙的,但这就是生活。现在,当强迫症让人们在“return”表达式周围使用括号时,就很重要了,就好像“return”是一个函数一样。:) 也使宏变得更加棘手。 (3认同)
  • 总是提示这种厌恶的示例是单行文件到字符串语法(也在该链接中提到)。它的每一部分都显得落后。您可能根本不期望有歧义,并强制从样本中删除多余的括号;您希望根据 SFINAE 通过消除过程解决歧义,但提前消除了声明以外的潜在候选人(SF**is**AE);令人沮丧的是,您可能会在编译后立即继续前进,认为任意括号解决了歧义,但他们*引入*了它。我想象中最让 CS101 教授烦恼的事。 (2认同)

101*_*010 29

这里引用东西:

  • decltype(auto)主要用于推断转发函数和类似包装器的返回类型,您希望类型完全"跟踪"您正在调用的某个表达式.

  • 例如,给定以下功能:


   string  lookup1();
   string& lookup2();
Run Code Online (Sandbox Code Playgroud)
  • 在C++ 11中,我们可以编写以下包装函数,这些函数会记住保留返回类型的引用:

   string  look_up_a_string_1() { return lookup1(); }
   string& look_up_a_string_2() { return lookup2(); }
Run Code Online (Sandbox Code Playgroud)
  • 在C++ 14中,我们可以自动化:

   decltype(auto) look_up_a_string_1() { return lookup1(); }
   decltype(auto) look_up_a_string_2() { return lookup2(); }
Run Code Online (Sandbox Code Playgroud)
  • 但是,decltype(auto)除此之外,并不打算成为广泛使用的功能.

  • 特别是,尽管它可以用于声明局部变量,但这样做可能只是一个反模式,因为局部变量的引用不应该依赖于初始化表达式.

  • 此外,它对您编写return语句的方式很敏感.

  • 例如,下面的两个函数具有不同的返回类型:


   decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }
   decltype(auto) look_up_a_string_2() { auto str = lookup2(); return(str); }
Run Code Online (Sandbox Code Playgroud)
  • 第一个返回string,第二个返回string&,这是对局部变量的引用str.

提案中您可以看到更多预期用途.

  • 为什么不只使用`auto`退货呢? (3认同)
  • 但这个问题仍然具有现实意义.返回类型是什么`auto lookup_a_string(){...}`?它总是非参考类型吗?因此需要`auto lookup_a_string() - > decltype(auto){...}`来强制允许(在某些情况下)返回引用? (3认同)
  • 另一个值得一提的示例是返回一个std :: vector元素。假设您有`template &lt;typename T&gt; struct S {auto&operator [](std :: size_t i){return v [i]; } std :: vector &lt;T&gt; v; }`。然后由于`std :: vector &lt;bool&gt;`的特殊性,`S &lt;bool&gt; :: operator []`将返回一个悬空的引用。将返回类型更改为`decltype(auto)`可以避免此问题。 (3认同)
  • @AaronMcDaid Deductible `auto` 是根据传递值模板定义的,所以是的,它不能作为参考。当然,请稍候 `auto` 可以是任何东西,包括参考。 (2认同)