33

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

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). 这不会破坏统一初始化的统一性吗?

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

4

3 回答 3

23

是的,它是一个错误。这是新事物,并在 2012 年 2 月的工作文件中投票(链接)。

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

于 2012-05-09T04:22:52.947 回答
9

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

§8.5.4/3

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

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

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

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

8.5.3/4

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

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


在 FDIS 草案中,等效段落的顺序颠倒了。这意味着 FDIS 草案 (n3290) 不允许 *lvalue*s 的大括号列表初始化。另一方面,阅读文本似乎很明显这是标准中的一个错误,并且意图具有 n3337 的顺序:

  • 否则,如果 T 是引用类型,则 T 引用的类型的纯右值临时是列表初始化的,并且引用绑定到该临时。

  • 否则,如果初始化列表只有一个元素,则从该元素初始化对象或引用;如果需要缩小转换(见下文)将元素转换为 T,则程序格式错误。

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

于 2012-05-09T04:13:20.620 回答
6

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


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

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

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

g++ 4.8 遵循 C++11 标准的已发布文本;然而,一旦这个问题暴露出来,g++ 4.9 就实现了更正的版本,即使使用了-std=c++11switch。

请注意,问题也不限于构造函数初始化程序列表,例如:S s; S &t{s};在 g++ 4.8 中不起作用,也不是S s; S &t = s; S &u { t };

于 2014-11-11T05:51:15.313 回答