本质上,变量的情况与函数的情况相同。这个想法是我们用一个decltype(auto)
变量存储函数调用的结果:
decltype(auto) result = /* function invocation */;
那么,result
是
现在我们需要一个新版forward
本来区分纯右值大小写和极值大小写:(forward
避免名称以防止 ADL 问题)
template <typename T>
T my_forward(std::remove_reference_t<T>& arg)
{
return std::forward<T>(arg);
}
然后使用
my_forward<decltype(result)>(result)
与 不同std::forward
的是,此函数用于转发decltype(auto)
变量。因此,它不会无条件地返回引用类型,并且应该使用 来调用decltype(variable)
它,可以是T
、T&
或T&&
,以便它可以区分左值、xvalue 和 prvalue。因此,如果result
是
非引用类型,然后使用非引用调用第二个重载T
,并返回非引用类型,从而产生纯右值;
一个左值引用类型,然后用 a 调用第一个重载T&
,并T&
返回一个左值;
一个右值引用类型,然后使用 a 调用第二个重载T&&
并T&&
返回,从而产生一个 xvalue。
这是一个例子。考虑您想要包装std::invoke
并打印一些内容到日志中:(示例仅用于说明)
template <typename F, typename... Args>
decltype(auto) my_invoke(F&& f, Args&&... args)
{
decltype(auto) result = std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
my_log("invoke", result); // for illustration only
return my_forward<decltype(result)>(result);
}
现在,如果调用表达式是
prvalue,result
则为非引用类型,函数返回非引用类型;
一个非常量左值,然后result
是一个非常量左值引用,并且函数返回一个非常量左值引用类型;
一个 const 左值,然后result
是一个 const 左值引用,函数返回一个 const 左值引用类型;
一个 xvalue,然后result
是一个右值引用类型,并且该函数返回一个右值引用类型。
给定以下功能:
int f();
int& g();
const int& h();
int&& i();
以下断言成立:
static_assert(std::is_same_v<decltype(my_invoke(f)), int>);
static_assert(std::is_same_v<decltype(my_invoke(g)), int&>);
static_assert(std::is_same_v<decltype(my_invoke(h)), const int&>);
static_assert(std::is_same_v<decltype(my_invoke(i)), int&&>);
(现场演示,仅移动测试用例)
如果auto&&
改为使用,代码将难以区分纯右值和 xvalue。