C++ gcc中的初始化列表错误?

C++ gcc中的初始化列表错误?,c++,c++11,gcc,initializer-list,C++,C++11,Gcc,Initializer List,考虑以下代码,其中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

考虑以下代码,其中
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;
}
因为从最派生的类(
D
)调用一次
B
s构造函数,这也决定了
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
,也一样,请参见)这没有意义,因为我在派生类中使用它,所以
受保护的
应该可以。如果我纠正了这一点,并将
B1
B2
的构造函数公开而不是保护,那么随着输出的增加,一切都会变得一团糟(请参阅)

99
0
10
Final: 10
因此,实际上,
B1
s和
B2
s构造函数中初始化
B
的部分仍在执行,甚至更改
x
的值。虚拟继承不应该是这种情况。记住,我唯一改变的是

  • B1
    B2
  • D
    的成员初始化列表中使用
    classname{}
    语法,而不是
    classname()
我无法相信gcc会出现这样一个基本的问题。但是我在本地机器上用clang测试了它,在那里,所有三个案例都按照预期编译和运行(如上面的第一个示例)。如果这不是一个bug,有人能告诉我我遗漏了什么吗


编辑:我的第一次搜索不知怎么没有找到它,但现在我找到了,至少显示了受保护/公共错误。然而,这是gcc-4.7,所以我希望它会在gcc-4.8中处理。那么,我是否应该得出结论,初始值设定项列表在gcc中根本就是一团糟

我不知道现在回答这个问题是否为时已晚,但您的代码在GCC 4.9.2中编译得很好

~$g++ -std=c++11 test.cpp 
~$./a.out 
99
Final: 99

~$gcc --version
gcc (GCC) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

关于虚拟基类构造函数的多个调用:我可以用以下代码(GCC 5.1.0)重现这个问题

我认为这不是符合C++标准的:

[class.base.init]/7

。。。 每个mem初始值设定项执行的初始化构成完整表达式。mem初始值设定项中的任何表达式都将作为执行初始化的完整表达式的一部分进行计算。mem初始化器 其中,mem初始值设定项id表示虚拟基类,在执行非最派生类的任何类的构造函数期间,该类将被忽略

当构造函数的调用更改为使用括号而不是大括号时,生成的可执行文件按预期工作,只调用V()一次

我已经为GCC创建了关于此问题的错误报告:


编辑:我没有注意到已经有一个关于这个的错误报告:

这在Clang3.5中可以使用,但在GCC4.9.0中失败。所以这可能是某人的编译器错误。是的,这和我在问题中说的差不多,只是它似乎在gcc-4.9.0中仍然存在!让我害怕的是,这会悄悄地导致对同一个对象的多个构造函数调用(如果所有构造函数都是公共的),违反了最基本的OOP原则之一……我在
GCC 5.1.1
@Galik和Oguk中得到了完全相同的错误:我在最新的GCC 5中遇到了这个问题和许多其他问题,
{bracked init}
在构造函数中。更糟糕的是,它们中的一些非常复杂(读作:fickle),使得最小的复制非常困难。即使这样,他们通常也不会急于解决这些问题,因为我想他们认为我们会明白——并且能够——使用
(括号)
。。。而不是花几个小时追逐其他的红鲱鱼和浪费我们的时间,这已经发生在我身上好几次感谢这些错误。真遗憾。
~$g++ -std=c++11 test.cpp 
~$./a.out 
99
Final: 99

~$gcc --version
gcc (GCC) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
#include <iostream>

struct V {
    V(){std::cout << "V()\n";}
};

struct A : virtual V {
    A() : V{} {std::cout << "A()\n";}
};

struct B : A {
    B(): V{}, A{} {std::cout << "B()\n";}
};

int main(int argc, char **argv) {
    B b{};
}
V()
V()
A()
B()