ChatGPT解决这个技术问题 Extra ChatGPT

decltype(auto) 有哪些用途?

在 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<class U, V> decltype(auto) first(std::pair<U, V>& p) { return p.first; } 的东西,但后来我意识到我必须使用 return (p.first);,这令人惊讶地工作(但 IIRC 这甚至是有意的)。
@user2485710 不确定它是否与优化有关,如果 decltype(auto) 可能导致某些内容被复制/移动到声明的对象中,则更有可能发生事故,这与预期相反。
在上面给出的示例中,如果 container 实际上是一个右值,我认为使用 decltype(auto) 可能会导致意外引用悬挂。但是,您可以通过 ContainerType 的值类型返回,并且复制省略应该给您与 decltype(auto) 相同的东西,但可以安全地作为参考 godbolt.org/z/GsYjxs
是的,这是另一个例子,容器的内部值被破坏,但我们要求从函数 godbolt.org/z/7jE5Me 中引用它

C
Community

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

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

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<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 auto 和 decltype(auto) 类型说明符指定了一个占位符类型,该类型将在以后被替换,或者通过从初始值设定项的推导,或者通过带有尾随返回类型的显式说明。 auto 类型说明符也用于表示 lambda 是通用 lambda。 2 占位符类型可以与 decl-specifier-seq、type-specifier-seq、conversion-function-id 或 trailing-return-type 中的函数声明符一起出现,在此类声明符有效的任何上下文中。如果函数声明符包含一个 trailing-return-type (8.3.5),它指定了函数声明的返回类型。如果函数声明的返回类型包含占位符类型,则函数的返回类型是从函数体中的返回语句推导出来的,如果有的话。

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

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 也可以完成这项工作,因为返回了结果无论如何,按价值计算...还是我错过了什么?
1
101010

引用 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); }

第一个返回字符串,第二个返回字符串&,这是对局部变量str的引用。

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


为什么不直接使用 auto 来返回?
@BЈовић 也可以使用广义返回类型推导(即 auto 返回),但 OP 专门要求使用 decltype(auto)
这个问题仍然是相关的。 auto lookup_a_string() { ... } 的返回类型是什么?它总是非引用类型吗?因此需要 auto lookup_a_string() ->decltype(auto) { ... } 强制允许(在某些情况下)返回引用?
@AaronMcDaid Deductible auto 是根据传递值模板定义的,所以是的,它不能作为参考。 Please-wait auto 当然可以是任何东西,包括参考。
另一个值得一提的示例是返回 std::vector 的元素。假设您有 template<typename T> struct S { auto & operator[](std::size_t i) { return v[i]; } std::vector<T> v; }。由于 std::vector<bool> 的特化,S<bool>::operator[] 将返回一个悬空引用。将返回类型更改为 decltype(auto) 可以避免此问题。