下面定义一个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
来恢复参数的“右值性”,即修改正文和decltype
to say
t < u ? std::forward<T>(t) : std::forward<U>(u)
但是,我无法解释为什么第一个定义不会产生错误。
鉴于我对转发和通用引用的理解,两者都t
将u
它们的参数类型推断为int&&
传递整数文字时的类型。但是,在 的主体中min
,参数具有名称,因此它们是左值。现在,条件运算符的真正复杂规则开始发挥作用,但我认为相关的行是:
- E2 [和] E3 都是相同类型的glvalues。在这种情况下,结果具有相同的类型和值类别。
因此返回类型也operator?:
应该是int&&
,不是吗?但是,(据我所知)Clang 和 g++ 都min(int&&, int&&)
返回了一个左值引用int&
,因此允许我分配给结果。
显然,我的理解存在差距,但我不确定我到底错过了什么。任何人都可以向我解释到底发生了什么吗?
编辑:
正如 Niall 正确指出的那样,这里的问题不在于条件运算符(它int&&
按预期返回类型的左值),而在于decltype
. decltype
说的规则
如果表达式的值类别是左值,则decltype指定T&
所以函数的返回值变成了int&& &
,通过 C++11 的引用折叠规则变成了普通的int&
(与我的预期相反int&&
)。
但是如果我们使用std::forward
,我们会将 (back) 的第二个和第三个参数operator?:
转换为右值——特别是 xvalues。由于 xvalues 仍然是 glvalues(你在后面跟上吗?),同样的条件运算符规则适用,我们得到相同类型和值类别的结果:即 an int&&
which 是 xvalue。
现在,当函数返回时,它会触发不同的decltype
规则:
如果表达式的值类别是xvalue,则decltype指定T&&
这一次,引用折叠给了我们int&& && = int&&
,更重要的是,函数返回了一个 xvalue。这使得分配给返回值是非法的,就像我们想要的那样。