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

时间:2021-02-27 22:44:58

That is, why does this:

这就是为什么:

struct S {};

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

    S& s;
};

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

give me a compiler error with GCC 4.7:

给我一个编译错误与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>'

?

吗?

To fix the error, I have to change the s{s} to s(s). Doesn't this break the, erm, uniformity of uniform initialization?

要修复错误,我必须将s{s}更改为s(s)。这不是打破了均匀初始化的均匀性吗?

EDIT: I tried with clang, and clang accepts it, so perhaps it's a GCC bug?

编辑:我尝试了clang, clang接受了,所以可能是一个GCC bug?

3 个解决方案

#1


21  

Yes, its a bug. This is something new and was voted in the working paper in February 2012 (link).

是的,它的一个缺陷。这是一个新的发现,并在2012年2月的工作文件(link)中投票。

Nicol Bolas makes a good point in that gcc is actually the conforming compiler according to the FDIS approved C++11 standard because the changes to the working paper were made after that.

Nicol Bolas很好地指出了gcc实际上是符合FDIS的符合标准的编译器,因为在此之后对工作文件进行了修改。

#2


8  

I believe that to be an error in the compiler. The two paragraphs that deal with reference initialization through list-initialization are (in n3337):

我认为这是编译器的错误。通过列表初始化处理引用初始化的两段是(n3337):

§8.5.4/3

§8.5.4/3

List-initialization of an object or reference of type T is defined as follows:

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

  • Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.

    否则,如果初始化器列表中只有一个E类型的元素,而T不是引用类型,或者其引用类型与E相关,则该对象或引用从该元素初始化;如果要将元素转换为T,需要进行收缩转换(见下面),则程序的格式不合适。

  • Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary. [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. — end note ]

    否则,如果T是一个引用类型,那么由T引用的类型的prvalue临时值是列表初始化的,引用被绑定到这个临时。[注意:如果引用类型是对非const类型的lvalue引用,则与往常一样,绑定将失败,并且程序是病态的。——结束注意)

The compiler seems to be applying the last paragraph, when it should be applying the first, as reference-related is defined as

编译器似乎在应用最后一段,当它应该应用第一段时,引用相关的定义为

8.5.3/4

8.5.3/4

Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2.

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

In the case of the question, the types of the reference and the initializer inside the brace-initialization-list are exactly the same, which means that the initialization should be valid.

对于这个问题,引用的类型和brace- initialize -list中的初始化器是完全相同的,这意味着初始化应该是有效的。


In the FDIS draft, the equivalent paragraphs had the order reversed. The implication is that the FDIS draft (n3290) did not allow for brace-list-initialization of *lvalue*s. On the other hand, reading the text it seems obvious that it is a bug in the standard and that the intention was having the order of n3337:

在FDIS草案中,对等段落的顺序颠倒了。这意味着FDIS草案(n3290)不允许对*lvalue*s进行带列表初始化。另一方面,阅读文本似乎很明显,它是标准中的一个缺陷,意图是有n3337的顺序:

  • Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary.

    否则,如果T是一个引用类型,那么T引用的类型的prvalue临时值将被列初始化,并且引用将绑定到那个临时类型。

  • Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.

    否则,如果初始化器列表只有一个元素,则从该元素初始化对象或引用;如果要将元素转换为T,需要进行收缩转换(见下面),则程序的格式不合适。

The order in that document means that because all reference types are handled by the first clause, mentioning reference in the following paragraph would make no sense.

该文档中的顺序意味着,因为所有引用类型都是由第一个子句处理的,因此在接下来的段落中提到引用是没有意义的。

#3


4  

(Note: I'm writing this answer with the benefit of 2 years of hindsight since the original question; and to put some of the information from comments into an actual answer so that it is searchable).

(注:我写这个答案是基于自最初的问题以来的两年的后知后觉;并把一些来自评论的信息放到一个实际的答案中,这样它就可以被搜索到)。


Of course, initializing a reference of type S& with a reference also of type S& is supposed to bind directly.

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

The problem is a defect in the C++11 standard and was addressed by DR1288. The corrected text appears in C++14.

问题是c++ 11标准中的一个缺陷,DR1288解决了这个问题。更正后的文本出现在c++ 14中。

The Committee has clarified that the corrected text is what was intended for C++11, and so a "conforming compiler" should implement the corrected version.

委员会已经澄清,修改后的文本是用于c++ 11的,因此“符合标准的编译器”应该实现修改后的版本。

g++ 4.8 followed the published text of the C++11 standard; however once this issue came to light, g++ 4.9 implemented the corrected version, even with -std=c++11 switch.

g+ 4.8遵循c++ 11标准发布的文本;然而,一旦这个问题浮出水面,g++ 4.9实现了修正后的版本,即使使用了-std=c++11开关。

Note that the problem is not confined to constructor initializer lists either, e.g.: S s; S &t{s}; doesn't work in g++ 4.8, nor does S s; S &t = s; S &u { t };

注意,这个问题也不仅限于构造函数初始化器列表,例如:S;科技{年代};不能在g++ 4.8中工作,S也不行;科技= S;S和u;

#1


21  

Yes, its a bug. This is something new and was voted in the working paper in February 2012 (link).

是的,它的一个缺陷。这是一个新的发现,并在2012年2月的工作文件(link)中投票。

Nicol Bolas makes a good point in that gcc is actually the conforming compiler according to the FDIS approved C++11 standard because the changes to the working paper were made after that.

Nicol Bolas很好地指出了gcc实际上是符合FDIS的符合标准的编译器,因为在此之后对工作文件进行了修改。

#2


8  

I believe that to be an error in the compiler. The two paragraphs that deal with reference initialization through list-initialization are (in n3337):

我认为这是编译器的错误。通过列表初始化处理引用初始化的两段是(n3337):

§8.5.4/3

§8.5.4/3

List-initialization of an object or reference of type T is defined as follows:

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

  • Otherwise, if the initializer list has a single element of type E and either T is not a reference type or its referenced type is reference-related to E, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.

    否则,如果初始化器列表中只有一个E类型的元素,而T不是引用类型,或者其引用类型与E相关,则该对象或引用从该元素初始化;如果要将元素转换为T,需要进行收缩转换(见下面),则程序的格式不合适。

  • Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary. [ Note: As usual, the binding will fail and the program is ill-formed if the reference type is an lvalue reference to a non-const type. — end note ]

    否则,如果T是一个引用类型,那么由T引用的类型的prvalue临时值是列表初始化的,引用被绑定到这个临时。[注意:如果引用类型是对非const类型的lvalue引用,则与往常一样,绑定将失败,并且程序是病态的。——结束注意)

The compiler seems to be applying the last paragraph, when it should be applying the first, as reference-related is defined as

编译器似乎在应用最后一段,当它应该应用第一段时,引用相关的定义为

8.5.3/4

8.5.3/4

Given types “cv1 T1” and “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2.

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

In the case of the question, the types of the reference and the initializer inside the brace-initialization-list are exactly the same, which means that the initialization should be valid.

对于这个问题,引用的类型和brace- initialize -list中的初始化器是完全相同的,这意味着初始化应该是有效的。


In the FDIS draft, the equivalent paragraphs had the order reversed. The implication is that the FDIS draft (n3290) did not allow for brace-list-initialization of *lvalue*s. On the other hand, reading the text it seems obvious that it is a bug in the standard and that the intention was having the order of n3337:

在FDIS草案中,对等段落的顺序颠倒了。这意味着FDIS草案(n3290)不允许对*lvalue*s进行带列表初始化。另一方面,阅读文本似乎很明显,它是标准中的一个缺陷,意图是有n3337的顺序:

  • Otherwise, if T is a reference type, a prvalue temporary of the type referenced by T is list-initialized, and the reference is bound to that temporary.

    否则,如果T是一个引用类型,那么T引用的类型的prvalue临时值将被列初始化,并且引用将绑定到那个临时类型。

  • Otherwise, if the initializer list has a single element, the object or reference is initialized from that element; if a narrowing conversion (see below) is required to convert the element to T, the program is ill-formed.

    否则,如果初始化器列表只有一个元素,则从该元素初始化对象或引用;如果要将元素转换为T,需要进行收缩转换(见下面),则程序的格式不合适。

The order in that document means that because all reference types are handled by the first clause, mentioning reference in the following paragraph would make no sense.

该文档中的顺序意味着,因为所有引用类型都是由第一个子句处理的,因此在接下来的段落中提到引用是没有意义的。

#3


4  

(Note: I'm writing this answer with the benefit of 2 years of hindsight since the original question; and to put some of the information from comments into an actual answer so that it is searchable).

(注:我写这个答案是基于自最初的问题以来的两年的后知后觉;并把一些来自评论的信息放到一个实际的答案中,这样它就可以被搜索到)。


Of course, initializing a reference of type S& with a reference also of type S& is supposed to bind directly.

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

The problem is a defect in the C++11 standard and was addressed by DR1288. The corrected text appears in C++14.

问题是c++ 11标准中的一个缺陷,DR1288解决了这个问题。更正后的文本出现在c++ 14中。

The Committee has clarified that the corrected text is what was intended for C++11, and so a "conforming compiler" should implement the corrected version.

委员会已经澄清,修改后的文本是用于c++ 11的,因此“符合标准的编译器”应该实现修改后的版本。

g++ 4.8 followed the published text of the C++11 standard; however once this issue came to light, g++ 4.9 implemented the corrected version, even with -std=c++11 switch.

g+ 4.8遵循c++ 11标准发布的文本;然而,一旦这个问题浮出水面,g++ 4.9实现了修正后的版本,即使使用了-std=c++11开关。

Note that the problem is not confined to constructor initializer lists either, e.g.: S s; S &t{s}; doesn't work in g++ 4.8, nor does S s; S &t = s; S &u { t };

注意,这个问题也不仅限于构造函数初始化器列表,例如:S;科技{年代};不能在g++ 4.8中工作,S也不行;科技= S;S和u;