来自 C++ lambda 的 auto&& return 类型

auto&& return type from a C++ lambda

我有兴趣了解尾随 auto&& return 类型的确切含义,特别是与 decltype(auto) 的区别,后者在这里不起作用,以及未指定的 return 类型,这也不起作用。

在下面的代码中,fn return 是参数的 x_ 字段。当参数是左值时,x_ 作为左值返回,等等

fn_bad[123] 的示例中,似乎 return int,即使提供了左值参数。我明白为什么 -> auto 会导致这种情况,但我预计 -> decltype(auto) 到 return int&。为什么只有 -> auto&& 有效?

#include <utility>

struct Foo { int x_; };

int main() {
  auto fn_bad1 = [](auto&& foo) -> decltype(auto) { return std::forward<decltype(foo)>(foo).x_; };
  auto fn_bad2 = [](auto&& foo) -> auto           { return std::forward<decltype(foo)>(foo).x_; };
  auto fn_bad3 = [](auto&& foo)                   { return std::forward<decltype(foo)>(foo).x_; };
  auto fn      = [](auto&& foo) -> auto&&         { return std::forward<decltype(foo)>(foo).x_; };
  Foo a{};
  fn(a) = fn(Foo{100}); // doesn't compile with bad1, bad2, bad3
}

but I expected -> decltype(auto) to return int&

这是 decltype

的预期行为

(强调我的)

Inspects the declared type of an entity or the type and value category of an expression.

1) If the argument is an unparenthesized id-expression or an unparenthesized class member access expression, then decltype yields the type of the entity named by this expression.

因此 decltype(auto)std::forward<decltype(foo)>(foo).x_ 上的结果产生数据成员的类型 x_,即 int.

如果你加括号为

[](auto&& foo) -> decltype(auto) { return (std::forward<decltype(foo)>(foo).x_); };
//                                        ^                                   ^

然后

2) If the argument is any other expression of type T, and

a) if the value category of expression is xvalue, then decltype yields T&&;
b) if the value category of expression is lvalue, then decltype yields T&;
c) if the value category of expression is prvalue, then decltype yields T.

Note that if the name of an object is parenthesized, it is treated as an ordinary lvalue expression, thus decltype(x) and decltype((x)) are often different types.

然后,正如您所说,当将左值传递给 lambda 时,表达式 (std::forward<decltype(foo)>(foo).x_) 是一个左值,那么 return 类型将是 int&;当传递一个右值时,表达式是一个 xvalue,那么 return 类型将是 int&&(这可能会导致悬空引用问题)。

对于第二种情况,根据 template argument deduction 的正常规则,每当传递左值或右值时,return 类型总是 int.

第三种情况与第二种情况相同。

对于第 4 种情况,应用 forwarding reference 的特殊规则,然后当 return 表达式为左值时,return 类型将为 int&,并且 int&& 当 return 表达式为右值时。