【问题标题】:Are there any realistic use cases for `decltype(auto)` variables?`decltype(auto)` 变量是否有任何实际的用例?
【发布时间】:2019-12-17 16:15:15
【问题描述】:

根据我的个人经验和对What are some uses of decltype(auto)? 等问题的咨询答案,我可以找到很多将decltype(auto) 用作函数返回类型占位符的有价值的用例。

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

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

那么,decltype(auto) 变量有什么实际的用例吗?或者这个功能只有在用作返回类型占位符时才有用?


你如何定义“现实”?

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

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

【问题讨论】:

  • 你如何定义“现实”?
  • @NicolBolas:我正在寻找一些提供价值的用例(即它不仅仅是一个展示该功能如何工作的示例),其中decltype(auto) 是完美的选择,与@ 等替代方案相比987654331@ 或根本不声明变量。域无关紧要,它可能是一些晦涩的元编程极端案例。但是这个例子需要让我去“嘿,这很聪明!” 并且使用任何其他功能来实现相同的效果将需要更多样板或有某种缺点。抱歉,我不能更准确。
  • @Eljay:我受宠若惊!我可以给你很多关于decltype(auto)-returning 函数的例子......但是现在变量让我感到困惑:)

标签: c++ c++14 auto type-deduction decltype-auto


【解决方案1】:

本质上,变量的情况与函数的情况相同。我们的想法是,我们将函数调用的结果存储在一个 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&amp;T&amp;&amp;,这样就可以区分左值、x值和prvalues。因此,如果result

  • 非引用类型,然后用非引用T调用第二个重载,返回非引用类型,产生prvalue;

  • 一个左值引用类型,然后用T&amp;调用第一个重载,返回T&amp;,得到一个左值;

  • 一个右值引用类型,然后用T&amp;&amp;调用第二个重载,并返回T&amp;&amp;,产生一个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&&>);

(live demo, move only test case)

如果改用auto&amp;&amp;,代码将难以区分纯右值和xvalues。

【讨论】:

  • 我认为这是朝着正确方向迈出的一步,但是当f 返回 prvalue 时,您的示例会导致不必要的复制,并禁止 强制复制省略。在这里看看我的版本:gcc.godbolt.org/z/awuVxy
  • 在此处发布了后续问题:stackoverflow.com/questions/57444893/… - 我不相信decltype(auto) 是解决此问题的最佳方法。 on_scope_success 解决方案看起来更干净。
  • @VittorioRomeo:“我不相信 decltype(auto) 是解决这个问题的最佳方法。”这就是让你的问题基于意见的原因:谁决定什么“最好”是什么? LF 的代码可读性极强,而您的替代示例......很奇怪。目前尚不清楚为什么您正在做您正在做的事情或这样做的目的是什么。只是为了也许避免复制/移动(因为几乎每个编译器都会 NVRO 代码),这是很多额外的代码。
  • @VittorioRomeo:确实,您自己对“现实”的定义明确指出“使用任何其他功能来实现相同的效果将需要更多样板”。好吧,您的替代方案肯定需要更多样板。所以根据你自己的定义,这是一个“现实”的例子。
  • @NicolBolas:这与 NRVO 无关,此答案提供的版本引入了额外的 copy!见gcc.godbolt.org/z/awuVxy。我不认为这是一个小缺点,我认为这是一个主要缺陷。例如,它防止使用仅移动对象。所以不,它没有达到与我的版本“相同的效果”。如果额外的副本无关紧要,我们不妨使用autoauto&amp;&amp; 而不是decltype(auto)
【解决方案2】:

可能不是一个很深的答案,但基本上decltype(auto)was proposed 用于返回类型推导,能够在返回类型实际上是引用时推导引用(与普通的auto相反,它永远不会推断参考,或auto&amp;&amp; 将始终这样做)。

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

【讨论】:

  • 我不明白 auto&& 是如何让你声明一个常量变量(引用永远不是顶层的 const),而 decltype(auto) 允许你毫无问题地做到这一点。
  • @L.F.抱歉回复晚了,可能我表达不完整:我的意思是在某些情况下,您可以通过在auto&amp;&amp; 中添加const 修饰符来转换为常量,而decltype(auto) 不允许您这样做。我知道不可能在所有情况下都使用(如int a; int&amp; b=a; const auto&amp;&amp; c=b;),但在int foo() { return 0; } const auto&amp;&amp; a=foo(); 这样的情况下。可能不是很有用,但可能。无论如何,如果我遗漏了什么,请纠正我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-29
  • 1970-01-01
  • 2023-03-28
相关资源
最近更新 更多