【问题标题】:What are some uses of decltype(auto)?decltype(auto) 有哪些用途?
【发布时间】:2014-07-29 08:54:27
【问题描述】:

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

还有其他例子可以说明这种新的语言功能有用吗?

【问题讨论】:

  • 这篇文章基本上建议尽量避免使用这种习惯用法,因为使用它时,您为编译器提供的优化选项较少stackoverflow.com/a/20092875/2485710
  • 我曾经将decltype(auto) 用于类似于template&lt;class U, V&gt; decltype(auto) first(std::pair&lt;U, V&gt;&amp; p) { return p.first; } 的东西,尽管后来我意识到我必须使用return (p.first);,这令人惊讶地工作(但IIRC 这甚至是有意的)。
  • 在您上面给出的示例中,如果 container 实际上是一个右值,我认为使用 decltype(auto) 可能会导致意外引用悬挂。但是,您可以通过 ContainerType 的值类型返回,复制省略号应该给您与 decltype(auto) 相同的东西,但可以安全地作为参考 godbolt.org/z/GsYjxs
  • 是的,这是另一个例子,容器的内部值被销毁,但我们要求从函数godbolt.org/z/7jE5Me引用它

标签: c++ auto c++14 decltype return-type-deduction


【解决方案1】:

泛型代码中的返回类型转发

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

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)...); 
}

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

前几天this Q&A中,当模板的返回类型指定为decltype(iter(Int&lt;i-1&gt;{}))而不是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 类型说明符也用于表示 lambda 是 通用 lambda。

2 占位符类型可以出现在decl-specifier-seq、type-specifier-seq、 转换函数 ID 或尾随返回类型,在此类声明符有效的任何上下文中。如果函数 声明器包括一个尾随返回类型(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)

【讨论】:

  • (i)i 的不同行为是 C++14 中的新事物吗?
  • @Danvil decltype(expr)decltype((expr)) 在 C++11 中已经不同,这概括了这种行为。
  • 我刚刚学会了这个,感觉就像一个糟糕的设计决定......在括号的语法含义中添加了一个准时的细微差别。
  • 总是提示这种厌恶的例子是单行文件到字符串的语法(也在那个链接中提到)。它的每一部分似乎都落后了。您可能根本不会期望歧义,并强制从样本中删除多余的括号;根据 SFINAE,您可能希望通过消除过程来解决歧义,但会提前消除声明以外的潜在候选人(SFisAE);并且一旦编译认为任意括号解决了歧义,您可能会感到沮丧,但他们引入它。我想最让 CS101 教授烦恼的。
  • @TemplateRex:关于在引用的问题中延迟返回类型解析:据我所知,在特定场景中,一个简单的auto 就可以完成这项工作同样,因为结果是按值返回的……还是我错过了什么?
【解决方案2】:

引用here的东西:

  • 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&amp;,这是对局部变量str的引用。

proposal你可以看到更多的预期用途。

【讨论】:

  • 为什么不直接使用auto作为回报?
  • @BЈовић 也可以使用广义返回类型推导(即auto return),但 OP 专门要求使用 decltype(auto)
  • 这个问题仍然是相关的。 auto lookup_a_string() { ... } 的返回类型是什么?它总是非引用类型吗?因此需要auto lookup_a_string() -&gt;decltype(auto) { ... } 强制允许(在某些情况下)返回引用?
  • @AaronMcDaid Deductible auto 是根据传递值模板定义的,所以是的,它不能作为参考。请稍等,auto 当然可以是任何东西,包括参考。
  • 另一个值得一提的例子是返回一个std::vector 的元素。假设你有template&lt;typename T&gt; struct S { auto &amp; operator[](std::size_t i) { return v[i]; } std::vector&lt;T&gt; v; }。然后S&lt;bool&gt;::operator[] 将返回一个悬空引用,因为std::vector&lt;bool&gt; 的特化。将返回类型更改为decltype(auto) 可以规避此问题。
猜你喜欢
  • 2011-04-16
  • 1970-01-01
  • 1970-01-01
  • 2021-11-18
  • 2011-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多