实际上,变量的用途与函数相同。其思想是我们使用 decltype(auto)
变量存储函数调用的结果:
decltype(auto) result = /* function invocation */;
那么,result
是
现在我们需要一个新版本的 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)
与
std::forward
不同,此函数用于转发
decltype(auto)
变量。 因此,它不会无条件地返回引用类型,并且应该使用
decltype(variable)
调用,其中可以是
T
,
T&
或
T&&
,以便区分lvalue,xvalue和prvalue。 因此,如果
result
是:
非引用类型,则使用非引用T
调用第二个重载,并返回非引用类型,从而产生prvalue;
左值引用类型,则使用T&
调用第一个重载,并返回T&
,从而产生左值;
右值引用类型,则使用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);
return my_forward<decltype(result)>(result);
}
现在,如果调用表达式是:
一个 prvalue, 那么 result
是一个非引用类型,并且该函数返回一个非引用类型;
一个非常量 lvalue, 那么 result
是一个非常量 lvalue 引用,并且该函数返回一个非常量 lvalue 引用类型;
一个常量 lvalue, 那么 result
是一个常量 lvalue 引用,并且该函数返回一个常量 lvalue 引用类型;
一个 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&&
,代码将很难区分 prvalues 和 xvalues。
decltype(auto)
是完美选择,与诸如auto&&
或根本不声明变量相比。领域并不重要,它可以是一些晦涩的元编程角落案例。但是这个例子需要让我感到聪明!使用任何其他功能来实现相同效果将需要更多的样板文件或具有某种缺点。很抱歉我不能更加精确。 - Vittorio Romeodecltype(auto)
返回函数的例子……但是变量目前让我感到困惑 :) - Vittorio Romeodecltype(auto)
变量来进行NRVO,就像这样:https://godbolt.org/z/9oz37Was1 - Artyer