decltype(auto)有什么用?


151

在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)];
}

还有其他使用此新语言功能的示例吗?


2
这篇文章主要建议尽量避免这种习惯用法,因为使用它的时候你给的少的优化选项,编译器stackoverflow.com/a/20092875/2485710
user2485710

我曾经使用decltype(auto)过类似的东西template<class U, V> decltype(auto) first(std::pair<U, V>& p) { return p.first; },尽管后来我意识到我不得不使用return (p.first);它出乎意料的效果(但是IIRC甚至是有意的)。
dyp 2014年

@ user2485710不确定具体是关于优化的,更多的是如果decltype(auto)可能导致某些东西被复制/移动到声明的对象中,则可能导致意外,这与预期相反。
underscore_d

Answers:


170

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

对于非通用代码,就像您给出的初始示例一样,您可以手动选择以获取引用作为返回类型:

auto const& Example(int const& i) 
{ 
    return i; 
}

但是在通用代码中,您希望能够完美地转发返回类型,而无需知道您要处理的是引用还是值。decltype(auto)给您这种能力:

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

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

在几天前的问答中,当模板的返回类型指定为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; }

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或tailing-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)

17
(i)vs 的不同行为i在C ++ 14中是新事物吗?
Danvil 2014年

14
@Danvil decltype(expr)decltype((expr))C ++ 11中已经有所不同,这概括了该行为。
TemplateRex

13
我刚刚学到了这一点,感觉就像是一个糟糕的设计决定……在括号的语法含义中添加了一点点的细微差别。
卡勒(Kahler)

总是引起这种厌恶的示例是单行文件到字符串的语法(在该链接中也提到了)。它的每个部分似乎都是落后的。您可能根本不会期望模棱两可,而会强制性地从样本中删除多余的括号。您可能希望根据SFINAE通过消除过程解决歧义,但是会预先消除声明以外的其他候选对象(SF AE);令人沮丧的是,一旦编译出来以为任意括号解决了歧义,您可能会继续前进,但是他们引入了歧义。我想象中的CS101教授最讨厌。
John P

@TemplateRex:关于延迟所引用问题中的返回类型解析:据我所知,在特定情况下,一个简单的方法auto也可以完成该任务,因为无论如何结果都是按值返回的……还是我错过了什么东西
阿空加瓜

36

这里引用东西:

  • decltype(auto)主要用于推导转发函数和类似包装的返回类型,在这种情况下,您希望该类型精确“跟踪”正在调用的某些表达式。

  • 例如,给定以下功能:


   string  lookup1();
   string& lookup2();

  • 在C ++ 11中,我们可以编写以下包装函数,这些包装函数记得保留返回类型的引用性:

   string  look_up_a_string_1() { return lookup1(); }
   string& look_up_a_string_2() { return lookup2(); }

  • 在C ++ 14中,我们可以实现以下自动化:

   decltype(auto) look_up_a_string_1() { return lookup1(); }
   decltype(auto) look_up_a_string_2() { return lookup2(); }

  • 但是,除此以外,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); }

  • 第一个返回string,第二个返回string&,这是对局部变量的引用str

提案中,您可以看到更多的预期用途。


3
为什么不只使用auto退货?
BЈовић

@BЈовић也可以使用广义返回类型推导(即autoreturn),但是OP专门要求使用decltype(auto)
101010 2014年

3
这个问题仍然很重要。返回类型是auto lookup_a_string() { ... } 什么?它总是非引用类型吗?因此auto lookup_a_string() ->decltype(auto) { ... }需要强制(在某些情况下)返回引用吗?
亚伦·麦克戴德

@AaronMcDaid自付额auto是按值传递模板定义的,因此是的,不能作为参考。请稍等auto,当然可以包括参考。
curiousguy 2015年

4
另一个值得一提的示例是返回的元素std::vector。说你有template<typename T> struct S { auto & operator[](std::size_t i) { return v[i]; } std::vector<T> v; }。然后S<bool>::operator[]由于的特殊化,将返回一个悬空的引用std::vector<bool>。更改返回类型decltype(auto)可以避免此问题。
Xoph
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.