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;