有`decltype(auto)`变量的实际用例吗?

Vit*_*meo 27 c++ auto type-deduction c++14 decltype-auto

从我的个人经验以及从咨询答案到诸如decltype(auto)有什么用途的问题我可以找到许多有价值的用例decltype(auto)作为函数返回类型占位符

但是,我在努力思考decltype(auto)变量的任何有效(即有用,现实,有价值)用例时都非常费劲。我想到的唯一可能性是存储函数返回的结果以decltype(auto)供以后传播,但auto&&也可以在此使用,这样会更简单。

我什至搜索了我所有的项目和实验,其中391个出现的decltype(auto)都是返回类型的占位符。

那么,变量是否有实际的用例decltype(auto)还是仅当用作返回类型占位符时此功能才有用?


您如何定义“现实”?

我正在寻找一个能提供价值的用例(即,不仅仅是显示功能如何工作的示例)decltype(auto),与诸如auto&&或根本不声明变量的替代方案相比,哪里是最佳选择。

问题域无关紧要,它可能是一些晦涩的元编程极端案例或神秘的功能编程构造。但是,该示例需要使我接受“嘿,这很聪明/很漂亮!” 而使用任何其他功能来达到相同的效果将需要更多样板,或者存在某种缺陷。

L. *_* F. 17

本质上,变量的情况与函数相同。这个想法是我们用一个decltype(auto)变量存储函数调用的结果:

decltype(auto) result = /* function invocation */;
Run Code Online (Sandbox Code Playgroud)

result

  • 非引用类型(如果结果是prvalue),

  • 如果结果是左值,则为(可能是经过cv限定)左值引用类型,或者

  • 如果结果是xvalue,则返回rvalue引用类型。

现在我们需要一个新版本的forward来区分prvalue和xvalue :(forward避免使用名称以避免ADL问题)

template <typename T>
T my_forward(std::remove_reference_t<T>& arg)
{
    return std::forward<T>(arg);
}
Run Code Online (Sandbox Code Playgroud)

然后用

my_forward<decltype(result)>(result)
Run Code Online (Sandbox Code Playgroud)

不同于std::forward,此函数用于转发decltype(auto)变量。因此,它不会无条件返回引用类型,并且它应该与被称为decltype(variable),其可以是TT&,或T&&,以便它可以左值,xvalues,和prvalues之间进行区分。因此,如果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); // for illustration only
    return my_forward<decltype(result)>(result);
}
Run Code Online (Sandbox Code Playgroud)

现在,如果调用表达式为

  • prvalue,result则为非引用类型,函数返回非引用类型;

  • 一个非常量左值,result则为一个非常量左值引用,该函数返回一个非常量左值引用类型;

  • const左值,result则为const左值引用,该函数返回const左值引用类型;

  • 一个xvalue,则result是一个rvalue引用类型,并且该函数返回一个rvalue引用类型。

赋予以下功能:

int f();
int& g();
const int& h();
int&& i();
Run Code Online (Sandbox Code Playgroud)

以下断言成立:

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&&>);
Run Code Online (Sandbox Code Playgroud)

现场演示仅移动测试用例

如果auto&&使用,则代码在区分prvalue和xvalue时会遇到一些麻烦。

  • @NicolBolas:与NRVO无关,此答案提供的版本引入了额外的*副本*!参见https://gcc.godbolt.org/z/awuVxy。我不认为这是次要的缺点,而是主要的缺陷。例如,它防止使用仅移动对象。所以不,它没有达到与我的版本相同的效果。如果多余的副本无关紧要,我们不妨使用`auto`或`auto &amp;&amp;`而不是`decltype(auto)`。 (2认同)

cbu*_*art 5

可能不是一个很深的答案,但基本上decltype(auto) 被建议用于返回类型推导,以便能够在返回类型实际上是引用时推导引用(与auto从不推论或auto&&始终会引用的平原相反))。

它也可以用于变量声明这一事实并不一定意味着应该有比其他情况更好的方案。的确,decltype(auto)在变量声明中使用时,使用in变量声明只会使代码读取复杂化,因为对于变量声明而言,具有完全相同的含义。另一方面,该auto&&窗体允许您声明一个常量变量,而decltype(auto)不能。