28

从我的个人经验和咨询回答诸如decltype(auto) 有哪些用途?我可以找到很多有价值的用例decltype(auto)作为函数返回类型占位符

但是,我很难为decltype(auto)变量考虑任何有效的(即有用的、现实的、有价值的)用例。想到的唯一可能性是存储返回的函数的结果以decltype(auto)供以后传播,但auto&&也可以在那里使用,而且会更简单。

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

那么,变量是否有任何现实的用例?decltype(auto)或者这个特性只在用作返回类型占位符时才有用?


你如何定义“现实”?

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

问题域无关紧要,它可能是一些晦涩的元编程极端案例或神秘的函数式编程结构。然而,这个例子需要让我去“嘿,这很聪明/漂亮!” 并且使用任何其他功能来实现相同的效果将需要更多样板或具有某种缺点。

4

2 回答 2

18

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

decltype(auto) result = /* function invocation */;

那么,result

  • 如果结果是纯右值,则为非引用类型,

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

  • 如果结果是 xvalue,则为右值引用类型。

现在我们需要一个新版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)它,可以是TT&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。

于 2019-08-10T09:09:21.363 回答
6

可能不是一个非常深刻的答案,但基本上decltype(auto) 被提议用于返回类型推导,以便能够在返回类型实际上是引用时推导引用(与auto永远不会推导引用的平原相反,或者auto&&总是这样做)。

它也可以用于变量声明这一事实并不一定意味着应该有比其他场景更好的场景。事实上,使用decltype(auto)in 变量声明只会使代码阅读复杂化,因为对于变量声明,is 具有完全相同的含义。另一方面,auto&&表单允许您声明一个常量变量,而decltype(auto)不允许。

于 2019-08-14T14:16:29.403 回答