本质上,变量的情况与函数相同。这个想法是我们用 a 来存储函数调用的结果decltype(auto)
多变的:
decltype(auto) result = /* function invocation */;
Then, result
is
现在我们需要一个新版本forward
区分 prvalue 情况和 xvalue 情况:(名称forward
避免出现 ADL 问题)
template <typename T>
T my_forward(std::remove_reference_t<T>& arg)
{
return std::forward<T>(arg);
}
然后使用
my_forward<decltype(result)>(result)
Unlike std::forward
,该函数用于转发decltype(auto)
变量。因此,它不会无条件返回引用类型,并且应该使用decltype(variable)
,这可以是T
, T&
, or T&&
,这样它就可以区分左值、x值和纯右值。因此,如果result
is
非引用类型,则使用非引用调用第二个重载T
,并且返回非引用类型,从而产生纯右值;
左值引用类型,然后使用 a 调用第一个重载T&
, and T&
返回,产生左值;
右值引用类型,然后使用 a 调用第二个重载T&&
, and 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);
}
现在,如果调用表达式是
纯右值,那么result
是非引用类型,且函数返回非引用类型;
一个非常量左值,那么result
是一个非常量左值引用,并且该函数返回一个非常量左值引用类型;
一个 const 左值,那么result
是一个const左值引用,并且该函数返回一个const左值引用类型;
一个 x 值,那么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&&>);
(现场演示 https://wandbox.org/permlink/GojxyLZDePXN2wZG, 仅移动测试用例 https://wandbox.org/permlink/K0KDeFT8B72dCRLt)
If auto&&
相反,代码在区分纯右值和 xvalue 时会遇到一些问题。