以下是一个
这已经在Clang 3.5和g++ 4.9上进行了测试。
解决方案很简单,只需使用
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
来恢复参数的“rvalue-ness”,即修改主体和 decltype 以说
t < u ? std::forward<T>(t) : std::forward<U>(u)
然而,我无法解释为什么第一个定义不会产生错误。
根据我对转发和通用引用的理解,当传递整数字面量时,t
和u
都推断其参数类型为int&&
。然而,在min
函数体内,参数具有名称,因此它们是左值。现在,条件运算符的真正复杂规则开始发挥作用,但我认为相关行是:
- E2和E3都是相同类型的glvalue。在这种情况下,结果具有相同的类型和值类别。
因此,operator?:
的返回类型也应该是int&&
,不是吗?然而,(据我所知)Clang和g++都使min(int&&, int&&)
返回一个左值引用int&
,从而允许我将其赋值给结果。
显然我对此事的理解存在一些漏洞,但我不确定具体缺少什么。有人能向我解释这里到底发生了什么吗?
编辑:
正如Niall正确指出的那样,这里的问题不在于条件运算符(它返回了预期的int&&
类型的左值),而在于decltype
。 decltype
的规则是:
如果表达式的值类别是左值,则decltype指定为T&
因此,函数的返回值变成了int&& &
,根据C++11的引用折叠规则,它变成了普通的int&
(与我预期的int&&
相反)。
但是,如果我们使用std::forward
,我们将operator?:
的第二个和第三个参数(重新)转换为右值 - 具体来说是xvalue。由于xvalue仍然是glvalue(你还跟上了吗?),因此同样的条件运算符规则适用,并且我们得到相同类型和值类别的结果:即一个int&&
,它是一个xvalue。
现在,当函数返回时,它触发了不同的decltype
规则:
如果表达式的值类别是xvalue,那么decltype指定为T&&。这次,引用折叠给了我们int&& && = int&&
,更重要的是函数返回一个xvalue。这使得对返回值进行赋值是非法的,正如我们所希望的那样。
std::forward
而不是std::move
,以便函数仍然可以处理左值参数)。我很好奇第一个(错误的)定义为什么会产生这样的结果。 - Tristan Brindleauto min(...) { ... }
),并允许编译器推断,那么问题可能出在decltype()
规则上,它会返回int
。 - Niall