以下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)
但是,我无法解释为什么第一个定义不会产生错误。
鉴于我对转发和通用引用的理解,t
和u
在传递整数文字时将其参数类型推导为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&&
的左值),而是使用decltype
。 decltype
的规则说
如果表达式的值类别是左值,则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。这使得分配给返回值非法,就像我们一样。
答案 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。
由于涉及t
和u
的表达式是左值(它们是左值 - 命名为r值引用),因此返回值是左值引用。
在这种情况下,它会导致可能修改文字的潜在情况。使用"universal references"(或"forwarding references")和相关参考折叠规则时,需要谨慎使用转发。
正如您已经指出的那样,为了纠正这种情况,需要正确使用std::forward
,并且返回类型将是预期的。
有关std::forward
的更多详细信息,请参阅折叠here on SO。