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

HC4*_*ica 33 c++ reference initializer-list uniform-initialization c++11

也就是说,为什么这样:

struct S {};

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

    S& s;
};

int main()
{
    S s;
    T t{s};
}
Run Code Online (Sandbox Code Playgroud)

给我一个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>'
Run Code Online (Sandbox Code Playgroud)

要修复错误,我必须更改s{s}s(s).这不会打破统一初始化的统一性吗?

编辑:我试过clang,clang接受它,所以也许这是一个GCC错误?

Jes*_*ood 23

是的,这是一个错误.这是一个新的东西,并在2012年2月的工作文件中投票(链接).

根据FDIS批准的C++ 11标准,Nicol Bolas提出了一个很好的观点,即gcc实际上是符合标准的编译器,因为工作文件的更改是在此之后做出的.

  • @NicolBolas:我不确定你所说的内容是否反映了编译器社区的共识.编译器通常会对缺陷报告实施修复,并将其视为标准的一部分.毕竟,这就是标准委员会通过标记变更a*缺陷报告*的意思 - 这应该是标准中的内容. (5认同)

Dav*_*eas 9

我认为这是编译器中的错误.通过列表初始化处理引用初始化的两段是(在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的括号列表初始化.另一方面,阅读文本似乎很明显,它是标准中的错误,并且意图是n3337的顺序:

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

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

该文档中的顺序意味着因为所有引用类型都由第一个子句处理,所以在下一段中提及引用是没有意义的.


M.M*_*M.M 5

(注意:自编写原始问题起,我经过2年的回顾后才编写此答案;并将注释中的某些信息放入实际答案中,以便可以搜索)。


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

该问题是C ++ 11标准中的一个缺陷DR1288解决。更正后的文本显示在C ++ 14中。

委员会已经澄清,更正的文本是针对C ++ 11的,因此,“符合标准的编译器”应实现更正的版本。

g ++ 4.8遵循C ++ 11标准的发布文本;但是,一旦发现此问题,即使使用-std=c++11switch ,g ++ 4.9也会实现更正的版本。

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