【发布时间】:2015-01-10 21:43:15
【问题描述】:
考虑以下代码,其中B 是由D 通过B1 和B2 继承的虚拟基类:
#include <iostream>
class B
{
protected:
int x;
protected:
B(int x) : x{x}{std::cout << x << std::endl;}
};
class B1 : virtual public B
{
protected:
B1() : B(0){}
};
class B2 : virtual public B
{
protected:
B2() : B(10){}
};
class D : public B1, public B2
{
public:
D() : B(99), B1(), B2() {}
void print() {std::cout << "Final: " << x << std::endl;}
};
int main() {
D d;
d.print();
return 0;
}
参见工作示例here。我在B 的构造函数中使用输出,并在D 完全构造之后跟踪正在发生的事情。当我使用 g++-4.8.1 编译上述示例时,一切正常。它打印
99
Final: 99
因为Bs 的构造函数从最派生类 (D) 调用一次,这也决定了 x 的最终值。
现在是奇怪的部分:如果我换行
D() : B(99), B1(), B2() {}
到新的统一初始化语法,即
D() : B{99}, B1{}, B2{} {}
奇怪的事情发生了。一方面,它不再编译,出现错误
prog.cpp: In constructor ‘D::D()’:
prog.cpp:17:5: error: ‘B1::B1()’ is protected
B1() : B(0){}
^
prog.cpp:31:27: error: within this context
D() : B{99}, B1{}, B2{} {}
(对于B2 也是如此,请参阅here)这没有意义,因为我在派生类中使用它,所以protected 应该没问题。如果我对此进行更正并将B1 和B2 的构造函数设为public 而不是protected,那么一切都会变得一团糟(参见here),因为输出变为
99
0
10
Final: 10
所以,事实上B1s和B2s构造函数中初始化B的部分仍然被执行,甚至改变了x的值。这不应该是虚拟继承的情况。请记住,我唯一改变的地方是
-
B1和B2中的公共而不是受保护的构造函数 - 在
D的成员初始化列表中使用classname{}语法,而不是classname()。
我无法相信 gcc 中出现了这样一个基本的问题。但是我在本地机器上用 clang 对其进行了测试,所有三个案例都按预期编译和运行(即像上面的第一个示例一样)。如果这不是错误,有人可以指出我所缺少的吗?
编辑:我的第一次搜索不知何故没有提出,但现在我找到了this other question,至少显示了受保护/公共错误。然而,这是 gcc-4.7,所以我原以为它会在 gcc-4.8 中处理。那么,我是否应该得出结论,初始化列表在 gcc 中根本就搞砸了!?
【问题讨论】:
-
这适用于 clang 3.5,但适用于 gcc 4.9.0。所以这可能是某人的编译器错误。
-
是的,这几乎就是我在问题中所说的,只是它似乎在 gcc-4.9.0 中仍然存在!让我害怕的是,这可能会默默地导致对同一个对象的多个构造函数调用(如果所有构造函数都是公共的),违反了最基本的 OOP 原则之一……
-
我在
GCC 5.1.1中得到完全相同的错误。 -
@Galik 和 Oguk:我在最新的 GCC 5 中遇到了这个问题和许多其他问题,
{braced init}在构造函数中。更糟糕的是,其中一些非常复杂(阅读:变化无常),使得最小的复制变得困难。即便如此,他们通常也不急于解决这些问题,因为我认为他们认为我们会弄清楚 - 并且能够 - 改为使用(parentheses)... 而不是追逐其他红色几个小时的鲱鱼,浪费我们的时间,由于这些错误,这已经发生在我身上好几次了。真可惜。
标签: c++ c++11 gcc initializer-list