I face the well known "dreaded" diamond situation :
A
/ \
B1 B2
\ /
C
|
D
The class A
has, say the constructor A::A(int i)
. I also want to forbid a default instantiation of a A
so I declare the default constructor of A
as private
.
The classes B1
and B2
are virtually derived from A
and have some constructors and a protected
default constructor.
[edit]
The constructors of B1
and B2
don't call the default constructor of A
.
[reedit]
The default constructors of B1
and B2
don't call the default constructor of A
either.
[reedit]
[edit]
The class C
is an abstract class and has some constructors that don't call any of the A
, B1
or B2
constructors.
In the class D
, I call the constructor A::A(i)
and some constructor of C
.
So as expected, when D
is created, it first creates a A
to solve the dreaded diamond problem, then it creates B1
, B2
and C
. Therefore there is no call of the default constructor of A
in B1
, B2
and C
because if there was, it would create many instances of A
.
The compiler rejects the code because the default constructor of A
is private
. If I set it to protected
it compiles.
What I don't understand is that when I run the code, the default constructor of A
is never called (as it should be). So why doesn't the compiler allow me to set it as private
?
[edit] okay I'll write an example... but it hurts ;-)
class A{
public:
A(int i):i_(i){};
virtual ~A(){};
protected:
int i_;
private:
A():i_(0){};/*if private => compilation error, if protected => ok*/
};
class B1: public virtual A{
public:
B1(int i):A(i){};
virtual ~B1(){};
protected:
B1():A(0){};
};
class B2: public virtual A{
public:
B2(int i):A(i){};
virtual ~B2(){};
protected:
B2():A(0){};
};
class C: public B1, public B2{
public:
C(int j):j_(j){};
virtual ~C()=0;
protected:
int j_;
};
C::~C(){};
class D: public C{
public:
D(int i,int j):A(i),C(j){};
~D(){};
};
int main(){
D d(1,2);
}
The compiler says that in constructor of C
, A::A()
is private. I agree with this, but as C
is an abstract class, it can't be instantiated as a complete object (but it can be instantiated as a base class subobject, by instantiating a D
).
[edit]
I added the tag `language-lawer' on someone's recommendation.
C++ doesn't have an access control specifier for member functions that can only be called from a derived class, but a constructor for an abstract class can only be called from a derived class by definition of an abstract class.
The compiler cannot know in advance exactly which classes are instantiated (this is a runtime property), and it cannot know which constructors are potentially called before link-time.
The standard text (emphasis mine):
1) It makes no exception for abstract classes and can only be interpreted as saying that all constructors should do a (sometimes fake) attempt at calling virtual base constructors.
2) It says that at runtime such attempts are ignored.
Some committee members have stated a different opinion in DR 257:
(...quoted above)
There is no "silence". The general rule applies as there is no specific rule for abstract classes.
The suggestion cannot make "clearer" something that doesn't exist now.
Some committee members are taken their desire for reality and it is very wrong.
(snip example and discussion similar to OP's code)
The DR is marked "CD2": the committee agrees this was an issue and the language definition is changed to fix this issue.