为什么这个函数在给定rvalue参数的情况下返回左值引用?

时间:2014-10-14 07:21:05

标签: c++ c++11 conditional-operator rvalue-reference universal-reference

以下min函数的定义

template <typename T, typename U>
constexpr auto
min(T&& t, U&& u) -> decltype(t < u ? t : u)
{
    return t < u ? t : u;
}

有一个问题:编写

似乎是完全合法的
min(10, 20) = 0;

已经使用Clang 3.5和g ++ 4.9进行了测试。

解决方案很简单,只需使用std::forward来恢复&#34; rvalue-ness&#34;参数,即修改正文和decltype

t < u ? std::forward<T>(t) : std::forward<U>(u)

但是,我无法解释为什么第一个定义不会产生错误。


鉴于我对转发和通用引用的理解,tu在传递整数文字时将其参数类型推导为int&&。但是,在min的主体内,参数具有名称,因此它们是左值。现在,really complicated rules for the conditional operator come into play,但我认为相关的行是:

  
      
  • E2 [和] E3都是相同类型的glvalues。在这种情况下,结果具有相同的类型和值类别。
  •   

因此operator?:的返回类型也应该是int&&,如果不是的话?但是,(据我所知),Clang和g ++都min(int&&, int&&)返回左值引用int&,从而允许我分配给结果。

显然我的理解存在差距,但我不确定我错过了什么。任何人都可以向我解释一下这里发生了什么吗?


修改

正如Niall正确指出的那样,这里的问题不在于条件运算符(它按预期返回类型int&&的左值),而是使用decltypedecltype的规则说

  

如果表达式的值类别是左值,则decltype指定T&amp;

因此函数的返回值变为int&& &,它通过C ++ 11的引用折叠规则变为普通int&(与我期望的int&&相反)。

但是如果我们使用std::forward,我们将operator?:(返回)的第二个和第三个参数转换为rvalues - 特别是xvalues。由于xvalues仍然是glvalues(你在后面跟上吗?),同样的条件运算符规则适用,我们得到相同类型和值类别的结果:即int&&,它是xvalue。

现在,当函数返回时,它会触发不同的decltype规则:

  

如果表达式的值类别是xvalue,则decltype指定T&amp;&amp;

这一次,参考折叠给了我们int&& && = int&&,更重要的是,函数返回了一个xvalue。这使得分配给返回值非法,就像我们一样。

1 个答案:

答案 0 :(得分:7)

问题可能出在decltype()规则上。

暗示;如果删除了尾部返回类型

template <typename T, typename U>
constexpr auto
min(T&& t, U&& u)
{
    return t < u ? t : u;
}

允许编译器推导出return type is int

使用decltype

  

decltype ( expression )
  ...
  如果表达式的值类别是左值,则decltype指定T&amp;

取自cppreference

由于涉及tu的表达式是左值(它们是左值 - 命名为r值引用),因此返回值是左值引用。

在这种情况下,它会导致可能修改文字的潜在情况。使用"universal references"(或"forwarding references")和相关参考折叠规则时,需要谨慎使用转发。

正如您已经指出的那样,为了纠正这种情况,需要正确使用std::forward,并且返回类型将是预期的。


有关std::forward的更多详细信息,请参阅折叠here on SO