Consider the following code, where B
is a virtual base class inherited by D
through B1
and 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;
}
See working example here. I use outputs in B
's constructor and after D
has been completely constructed to keep track of what's going on. Everything works fine, when I compile the above example with g++-4.8.1. It prints
99
Final: 99
because B
s constructor is called once from the most derived class (D
) and that also determines the final value of x
.
Now comes the strange part: If I change the line
D() : B(99), B1(), B2() {}
to the new uniform initialization syntax, i.e.
D() : B{99}, B1{}, B2{} {}
strange things happen. For one, it doesn't compile anymore, with the error
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{} {}
(and the same for B2
, see here) which doesn't make sense because I am using it in a derived class, so protected
should be fine. If I correct for that and make the constructors of B1
and B2
public instead of protected, everything gets totally messed up (see here), as the output becomes
99
0
10
Final: 10
So, in fact, the parts of B1
s and B2
s constructors that initialize B
are still executed and even change the value of x
. This should not be the case for virtual inheritance. And remember, the only things I have changed are
B1
and B2
classname{}
syntax in member initialization list of D
instead of classname()
.I cannot believe such a basic thing goes wrong in gcc. But I tested it with clang on my local machine and there, all three cases compile and run as intended (i.e. like the first example above). If it's not a bug, can someone please point me to what I am missing?
EDIT: My first search somehow didn't bring it up, but now I found this other question, showing at least the protected/public error. However, this was gcc-4.7, so I would have expected it to be dealt with in gcc-4.8. So, should I conclude initializer lists are just fundamentally messed up in gcc!?
I don't know if it's too late to answer this, but your code compiles fine in 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.
Regarding multiple calls of virtual base class constructor: I could reproduce the issue with the following code (with GCC 5.1.0).
#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{};
}
This results in the following output:
V()
V()
A()
B()
I don't think this is correct accoring to the C++ standard:
[class.base.init]/7
... The initialization performed by each mem-initializer constitutes a full-expression. Any expression in a mem-initializer is evaluated as part of the full-expression that performs the initialization. A mem-initializer where the mem-initializer-id denotes a virtual base class is ignored during execution of a constructor of any class that is not the most derived class.
When the call of the A constructor is changed to use parenthesis instead of braces the resulting executable works as expected and only calls V() once.
I've created a bug report for GCC regarding this issue: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70818
Edit: I missed that there already has been a bug report about this: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55922
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With