为什么我不能使用统一初始化初始化初始化列表中的引用?

时间:2012-05-09 03:54:11

标签: c++ reference c++11 initializer-list uniform-initialization

也就是说,为什么会这样:

struct S {};

struct T
{
    T(S& s) : s{s} {}

    S& s;
};

int main()
{
    S s;
    T t{s};
}

给我一​​个GCC 4.7的编译器错误:

test.cpp: In constructor 'T::T(S&)':
test.cpp:5:18: error: invalid initialization of non-const reference of type 'S&' from an rvalue of type '<brace-enclosed initializer list>'

要解决此错误,我必须将s{s}更改为s(s)。这不会破坏统一初始化的erm,一致性吗?

编辑:我尝试使用clang,clang接受了,所以也许这是一个GCC错误?

3 个答案:

答案 0 :(得分:22)

是的,它是bug。这是新的事情,并在2012年2月的工作文件中进行了投票(link)。

Nicol Bolas指出,根据FDIS批准的C ++ 11标准,gcc实际上是符合标准的编译器,因为工作文件的更改是在此之后做出的。

答案 1 :(得分:8)

我认为这是编译器中的错误。通过 list-initialization 处理引用初始化的两段是(在n3337中):

  

§8.5.4/ 3

     

对象或类型T的引用的列表初始化定义如下:

     
      
  • 否则,如果初始化列表具有E类型的单个元素且T不是引用类型或其引用类型与E引用相关,则从该元素初始化对象或引用;如果将元素转换为T需要缩小转换(见下文),则程序格式不正确。

  •   
  • 否则,如果T是引用类型,则由T引用的类型的prvalue临时值被列表初始化,并且引用绑定到该临时值。 [注意:像往常一样,如果引用类型是非const类型的左值引用,则绑定将失败并且程序格式错误。 - 尾注]

  •   

编译器似乎应用了最后一段,应该应用第一段,因为与引用相关的被定义为

  

8.5.3 / 4

     

给定类型“cv1 T1”和“cv2 T2”,如果T1与T2的类型相同,则“cv1 T1”与“cv2 T2”引用相关,或者T1是T2的基类。

在问题的情况下,括号初始化列表中的引用类型和初始化程序完全相同,这意味着初始化应该是有效的。


在FDIS草案中,等效段落的顺序相反。这意味着FDIS草案(n3290)不允许* lvalue * s的大括号列表初始化。另一方面,阅读文本似乎很明显,它是标准中的 bug ,意图是n3337的顺序:

  
      
  • 否则,如果T是引用类型,则由T引用的类型的prvalue临时列表进行列表初始化,并且引用绑定到该临时类型。

  •   
  • 否则,如果初始化列表包含单个元素,则从该元素初始化对象或引用;如果将元素转换为T需要缩小转换(见下文),则程序格式不正确。

  •   

该文件中的顺序意味着因为所有参考类型都是由第一个条款处理的,所以在下一段中提及参考是没有意义的。

答案 2 :(得分:4)

(注意:我从原始问题开始,以2年的后见之处写出这个答案;并将评论中的一些信息放入实际答案中,以便可以搜索到。)


当然,使用类型为S&的引用初始化类型S&的引用应该直接绑定。

问题是C ++ 11标准中的缺陷,并由DR1288解决。更正的文本出现在C ++ 14中。

委员会澄清说,更正的文本是针对C ++ 11的,因此符合标准的编译器&#34;应该实施更正版本。

g ++ 4.8遵循C ++ 11标准的已发布文本;然而,一旦发现这个问题,即使使用-std=c++11切换,g ++ 4.9也会实现更正后的版本。

请注意,问题不仅限于构造函数初始值设定项列表,例如:S s; S &t{s};在g ++ 4.8中不起作用,S s; S &t = s; S &u { t };

也不起作用