采用三个返回纯右值、左值、x值的函数:
int f();
int& f(int);
int&& f(int, int);
并通过返回的函数调用它们
decltype(auto)
decltype(auto) returnsDecltypeOf(auto... x) {
return f(x...);
}
这似乎按照人们期望的方式工作:
static_assert(std::is_same_v<decltype(returnsDecltypeOf()), int>);
static_assert(std::is_same_v<decltype(returnsDecltypeOf(1)), int&>);
static_assert(std::is_same_v<decltype(returnsDecltypeOf(1,1)), int&&>);
但是如果我将
decltype(auto)
更改为 auto&&
,
auto&& returnsAutoRefRef(auto... x) {
return f(x...);
}
事情发生了变化:
static_assert(std::is_same_v<decltype(returnsAutoRefRef()), int&&>);
static_assert(std::is_same_v<decltype(returnsAutoRefRef(1)), int&>);
static_assert(std::is_same_v<decltype(returnsAutoRefRef(1,1)), int&&>);
我看到了明显的区别,
returnsAutoRefRef
总是返回一个引用,所以它可能会做错误的事情,例如在 f()
的情况下,它将通过 auto&&
返回值返回纯右值表达式,这将是一个悬空 int&&
。
因此,看起来
decltype(auto)
(或者,如果我们计划利用 SFINAE,甚至更好 -> decltype(return-expr)
)确实是将 return
表达式完美转发回调用者的方法。
所以我的问题是,我什么时候想回来
auto&&
?为什么我不能做decltype(auto)
?
据我对 C++ 的理解,前导返回类型
decltype(auto)
与前导 auto
+ 尾随 -> decltype(return expression)
是一样的,除了后者是 SFINAE 友好的而前者不是 。现在,我不确定这个 SFINAE 特定的事情对问题的答案有多大影响!
decltype(auto)
与auto&&
不同的情况正好有两种。
正如您所指出的,当您返回纯右值时,
decltype(auto)
按值返回,并且 auto&&
返回悬空右值引用(可能不合需要)。
第二种情况是直接返回实体。例如:
struct X {
int i;
decltype(auto) return_entity() { return i; }
decltype(auto) return_expression() { return (i); }
};
static_assert(std::is_same_v<decltype(X{}.return_entity()), int>);
static_assert(std::is_same_v<decltype(X{}.return_expression()), int&>);
(因为
decltype(i)
是int
,但是decltype((i))
是int&
)。
在所有其他场景中它们都是相同的。您可能需要使用
auto&&
来强调您正在返回参考信息,或者使用 decltype(auto)
来强调您正在完美转发某些内容。
std::forward_like
的文档显示为返回 auto&&
,因为它始终返回引用。
但是如果你可以返回纯右值,
decltype(auto)
显然是更好的选择。