Рассмотрим следующий код, где 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;
}
См. рабочий пример здесь. Я использую выходы в конструкторе B
и после того, как D
был полностью сконструирован, чтобы отслеживать, что происходит. Все работает нормально, когда я скомпилировал приведенный выше пример с g++ - 4.8.1. Он печатает
99
Final: 99
потому что конструктор B
вызывается один раз из самого производного класса (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
, см. здесь), что не имеет смысла, потому что я использую его в производном классе, поэтому protected
должно быть в порядке. Если я исправлю это и сделаю конструкторы B1
и B2
общедоступными, а не защищенными, все будет полностью испорчено (см. здесь), так как выход становится
99
0
10
Final: 10
Итак, на самом деле части конструкторов B1
и B2
, которые инициализируют B
, все еще выполняются и даже изменяют значение x
. Это не должно иметь место для виртуального наследования. И помните, единственное, что я изменил, - это
- public вместо защищенных конструкторов в
B1
иB2
- используйте
classname{}
синтаксис в списке инициализации членовD
вместоclassname()
.
Я не могу поверить, что такая базовая вещь идет не так, как в gcc. Но я протестировал его с помощью clang на моей локальной машине, и там все три случая скомпилируются и запускаются по назначению (т.е. Как первый пример выше). Если это не ошибка, может кто-то, пожалуйста, указать мне, чего я не вижу?
EDIT: мой первый поиск каким-то образом не вызвал его, но теперь я нашел этот другой вопрос, указав хотя бы защищенную/общедоступную ошибку. Однако это было gcc-4.7, поэтому я ожидал, что это будет рассмотрено в gcc-4.8. Итак, должен ли я заключить, что списки инициализаторов просто испорчены в gcc!?