临时const数组无法绑定到右值引用。

10
我有以下测试程序:
#include <iostream>
#include <type_traits>
#include <utility>

template<typename Ty, std::size_t N>
void foo(Ty (&&)[N])
{
    std::cout << "Ty (&&)[" << N << "]\t" << std::is_const<Ty>::value << '\n';
}

template<typename Ty, std::size_t N>
void foo(Ty (&)[N])
{
    std::cout << "Ty (&)[" << N << "]\t" << std::is_const<Ty>::value << '\n';
}

template<typename Ty>
using id = Ty;

int main()
{
    std::cout.setf(std::cout.boolalpha);

    foo(id<int[]>{1, 2, 3, 4, 5});
    foo(id<int const[]>{1, 2, 3, 4, 5}); // <-- HERE.
    int xs[]{1, 2, 3, 4, 5};
    foo(xs);
    int const ys[]{1, 2, 3, 4, 5};
    foo(ys);
    foo(std::move(xs));
    foo(std::move(ys));
}

我原本认为带箭头的代码行应该会调用与其上面非const调用类似的rvalue重载函数,但实际情况并非如此。

这是GCC中的一个bug吗?还是标准中有什么规定导致了选择lvalue重载函数?


有趣的是,Clang 正确地调用了右值重载。 - Xeo
好问题:我的标准的天真理解与你的一致。如果你想看输出,可以点击此处 - Yakk - Adam Nevraumont
这里有些有趣的事情:http://ideone.com/ptTJ8i -- 我的 const int 临时变量被当作 int&& 而不是 const int&& - Yakk - Adam Nevraumont
@Yakk,不存在“const int临时变量”。类型为“const int”的右值不存在,因为const和volatile只对对象有意义。非类/非数组类型的右值不引用对象,因此标准规定对于这种右值,“const”和“volatile”始终不存在。出于同样的原因,也不存在“int临时变量”:临时变量是其生命周期很快结束的对象。但是像“0”或“int()”这样的rvalue int并不引用对象,因此没有生命周期可停止。因此,规范没有说它们是临时变量(但在一个地方有一个错误)。 - Johannes Schaub - litb
@Mehrdad 在上面的评论中,我需要纠正一句话:确实存在prvalue常量整数,但它们全部都是在引用绑定期间由编译器合成的。在我看来,应该将其归类为xvalues,因为创建的目的是要有一个地址/对象进行绑定。const int&&可以绑定到 const intint 类型的 xvalues。(我上面说的只适用于prvalues,而不是rvalues)。 - Johannes Schaub - litb
显示剩余2条评论
1个回答

2
根据标准§12.2[class.temporary]

类类型的暂时值(temporary)会在各种不同的上下文中创建:将引用绑定到prvalue(8.5.3),返回prvalue(6.6.3),创建prvalue的转换(4.1、5.2.9、5.2.11、5.4),抛出异常(15.1),进入处理程序(15.3)以及在某些初始化中(8.5)。

因此,id<int const[]>{1, 2, 3, 4, 5}是一个暂时值,因此是一个prvalue §3.10 [basic.lval]:

rvalue(历史上称为rvalue,因为rvalue可以出现在赋值表达式的右侧)是xvalue、临时对象(12.2)或其子对象,或者与对象无关的值。

prvalue(“pure” rvalue)是不是xvalue的rvalue。

因此,应选择具有rvalue引用参数的重载函数。


所以基本上这是GCC的一个错误? - user541686
@Mehrdad:我想是这样的。正如Xeo在他的评论中指出的那样,Clang能够正确识别它。 - A. Mikhaylov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接