Diamond inheritance with additional class copy constructor - c++

So I was wondering lately what the copy constructor of an class will look if we have a base class A that is being virtually inherited by B and C, where B and C are inherited by D and D is inherited by E.
So I am a bit confused. Should D's copy constructor still have the base class's (A) constructor in its initializing list or is that the job of E now? Am I correct that E could inherit D non-virtually?

The implicitly generated copy constructor works fine, but if you wanted to explicitly write it yourself it could look like:
D(D const &o): A(o), B(o), C(o) {}
E(E const &o): A(o), D(o) {}
For initialization purposes, virtual base classes behave as if they are a direct base of the most-derived object. Any initializer in a subobject is ignored (e.g. in my code for E, the initialization D(o) actually does not initialize the A despite the fact that A is listed in D's copy constructor).
This is the same whether or not E inherits D virtually.
Link to further reading

Yes, A should still be in the initializing list and E does not need to inherit D virtually. The order of construction should be:
A() --> B() --> C() --> D()

Related

Why does importing constructors with 'using' cause a different overload to be selected? [duplicate]

I've got the following code:
class C {
public:
C(int) {}
C(const C&) {}
C() {}
};
class D : public C {
public:
using C::C;
};
int main() {
C c;
D d_from_c(c); // does not compile, copy ctor is not inherited
D d_from_int(1); // compiles, C(int) is inherited
}
Derived class should inherit all ctors of base except the default ctor (it is explained here). But why copy ctor is not inherited as well? Arguments from the related question are not acceptable here.
The code is compiled with g++ 4.8.1.
Because the standard says so. [class.inhctor]/p3, emphasis mine:
For each non-template constructor in the candidate set of inherited
constructors other than a constructor having no parameters or a
copy/move constructor having a single parameter, a constructor is
implicitly declared with the same constructor characteristics unless
there is a user-declared constructor with the same signature in the
complete class where the using-declaration appears or the constructor
would be a default, copy, or move constructor for that class.
Derived class should inherit all ctors of base except the default ctor
No, that's not true, see T.C.'s answer for the real rule.
The purpose of inheriting constructors is to say "the derived type can be created from the same arguments as the base type", but that isn't relevant for the base class' copy constructor, because a copy constructor is not just a way of saying how to create a type from a given argument.
A copy constructor is special, it's for copying an object of the same type.
A constructor D(const C&) would not used be for copying an object of the same type, because C is not the same type as D.
For a moment, we’ll assume ‘copy constructor inheritance’ is allowed.
Having your class structure intact, please consider following code for modified main method.
int main() {
C c;
D d;
D d_from_d(d);
D d_from_c(c); // does not compile, copy ctor is not inherited
D d_from_int(1); // compiles, C(int) is inherited
}
In D d_from_d(d), as a normal constructor call, there will be two copy constructor calls. One for C::C(const C&) and the other one is for compiler generated copy constructor for D. Having source object type in D (d in this case), C’s copy constructor can copy d’s C attributes while compiler generated D’s copy constructor can copy d’s D attribute.
But in D d_from_c(c) case, There is no problem for C’s copy constructor because, c’s C attributes can be copies by C’s copy constructor. But how does the compiler generated D’s copy constructor know the way to copy ‘D’s attributes from C’s object’. This is a conflict which should be avoided.
But, if you provide some sort of ‘weird copy constructor’ (you may need to a default constructor as well) like;
D(const C & c):C(c){}
Then,
calling D d_from_c(c);
is valid. Because, now we have explicitly provided a matching ‘copy’ constructor.
So, saying ‘Inheriting copy constructors are now allowed’ is invalid.

Cannot create subclass from implicit inherited superclass constructor c++ [duplicate]

I've got the following code:
class C {
public:
C(int) {}
C(const C&) {}
C() {}
};
class D : public C {
public:
using C::C;
};
int main() {
C c;
D d_from_c(c); // does not compile, copy ctor is not inherited
D d_from_int(1); // compiles, C(int) is inherited
}
Derived class should inherit all ctors of base except the default ctor (it is explained here). But why copy ctor is not inherited as well? Arguments from the related question are not acceptable here.
The code is compiled with g++ 4.8.1.
Because the standard says so. [class.inhctor]/p3, emphasis mine:
For each non-template constructor in the candidate set of inherited
constructors other than a constructor having no parameters or a
copy/move constructor having a single parameter, a constructor is
implicitly declared with the same constructor characteristics unless
there is a user-declared constructor with the same signature in the
complete class where the using-declaration appears or the constructor
would be a default, copy, or move constructor for that class.
Derived class should inherit all ctors of base except the default ctor
No, that's not true, see T.C.'s answer for the real rule.
The purpose of inheriting constructors is to say "the derived type can be created from the same arguments as the base type", but that isn't relevant for the base class' copy constructor, because a copy constructor is not just a way of saying how to create a type from a given argument.
A copy constructor is special, it's for copying an object of the same type.
A constructor D(const C&) would not used be for copying an object of the same type, because C is not the same type as D.
For a moment, we’ll assume ‘copy constructor inheritance’ is allowed.
Having your class structure intact, please consider following code for modified main method.
int main() {
C c;
D d;
D d_from_d(d);
D d_from_c(c); // does not compile, copy ctor is not inherited
D d_from_int(1); // compiles, C(int) is inherited
}
In D d_from_d(d), as a normal constructor call, there will be two copy constructor calls. One for C::C(const C&) and the other one is for compiler generated copy constructor for D. Having source object type in D (d in this case), C’s copy constructor can copy d’s C attributes while compiler generated D’s copy constructor can copy d’s D attribute.
But in D d_from_c(c) case, There is no problem for C’s copy constructor because, c’s C attributes can be copies by C’s copy constructor. But how does the compiler generated D’s copy constructor know the way to copy ‘D’s attributes from C’s object’. This is a conflict which should be avoided.
But, if you provide some sort of ‘weird copy constructor’ (you may need to a default constructor as well) like;
D(const C & c):C(c){}
Then,
calling D d_from_c(c);
is valid. Because, now we have explicitly provided a matching ‘copy’ constructor.
So, saying ‘Inheriting copy constructors are now allowed’ is invalid.

ladder-like C++ virtual inheritance

If I have a class inheritance relation like the following
a
/ \
b c
\ |
| d
\/ \
e f
\ /
g
Is the following the correct definition?
class A {};
class B: public virtual A {};
class C: public virtual A {};
class D: public C {};
class E: public B, public virtual D {};
class F: public virtual D {};
class G: public E, public F {};
I made both A and D virtually inherited because I assume each joint class need to be virtual.
Also I am not sure how C++ defines the constructor order of the above case. The link
https://isocpp.org/wiki/faq/multiple-inheritance#mi-vi-ctor-order says
The very first constructors to be executed are the virtual base
classes anywhere in the hierarchy. They are executed in the order they
appear in a depth-first left-to-right traversal of the graph of base
classes, where left to right refer to the order of appearance of base
class names.
After all virtual base class constructors are finished, the
construction order is generally from base class to derived class. The
details are easiest to understand if you imagine that the very first
thing the compiler does in the derived class’s ctor is to make a
hidden call to the ctors of its non-virtual base classes (hint: that’s
the way many compilers actually do it). So if class D inherits
multiply from B1 and B2, the constructor for B1 executes first, then
the constructor for B2, then the constructor for D. This rule is
applied recursively; for example, if B1 inherits from B1a and B1b, and
B2 inherits from B2a and B2b, then the final order is B1a, B1b, B1,
B2a, B2b, B2, D.
Note that the order B1 and then B2 (or B1a then B1b) is determined by
the order that the base classes appear in the declaration of the
class, not in the order that the initializer appears in the derived
class’s initialization list.
If so, is the order like?
A, D, B, C, D, F, G
I do not expect D is constructed before C. What is the correct constructor order?
When constructing an object of class G, the virtual base classes are initialized first. This results in the constructors for A and D being called. The constructor for D will construct it's base C object. Then the constructors for the non-base classes of G will be called, which would be E and F. The constructor for E will call the constructor for B. This gives the final order:
A C D B E F G
I added constructors that call printf to watch what happens (code available on Godbolt for the curious). The order I got, with both clang (version 6.0.0) and gcc (versions 6.4.0 and 7.3.0), is:
A
C
D
B
E
F
G
This matches what I'd expect. A and D have to be constructed first, but you can't construct a D without a C (imagine a more complex example where D's constructor called into C. Once all virtual inheritance has been satisfied, the constructors are called in the same order we expect.

Copy constructor is not inherited

I've got the following code:
class C {
public:
C(int) {}
C(const C&) {}
C() {}
};
class D : public C {
public:
using C::C;
};
int main() {
C c;
D d_from_c(c); // does not compile, copy ctor is not inherited
D d_from_int(1); // compiles, C(int) is inherited
}
Derived class should inherit all ctors of base except the default ctor (it is explained here). But why copy ctor is not inherited as well? Arguments from the related question are not acceptable here.
The code is compiled with g++ 4.8.1.
Because the standard says so. [class.inhctor]/p3, emphasis mine:
For each non-template constructor in the candidate set of inherited
constructors other than a constructor having no parameters or a
copy/move constructor having a single parameter, a constructor is
implicitly declared with the same constructor characteristics unless
there is a user-declared constructor with the same signature in the
complete class where the using-declaration appears or the constructor
would be a default, copy, or move constructor for that class.
Derived class should inherit all ctors of base except the default ctor
No, that's not true, see T.C.'s answer for the real rule.
The purpose of inheriting constructors is to say "the derived type can be created from the same arguments as the base type", but that isn't relevant for the base class' copy constructor, because a copy constructor is not just a way of saying how to create a type from a given argument.
A copy constructor is special, it's for copying an object of the same type.
A constructor D(const C&) would not used be for copying an object of the same type, because C is not the same type as D.
For a moment, we’ll assume ‘copy constructor inheritance’ is allowed.
Having your class structure intact, please consider following code for modified main method.
int main() {
C c;
D d;
D d_from_d(d);
D d_from_c(c); // does not compile, copy ctor is not inherited
D d_from_int(1); // compiles, C(int) is inherited
}
In D d_from_d(d), as a normal constructor call, there will be two copy constructor calls. One for C::C(const C&) and the other one is for compiler generated copy constructor for D. Having source object type in D (d in this case), C’s copy constructor can copy d’s C attributes while compiler generated D’s copy constructor can copy d’s D attribute.
But in D d_from_c(c) case, There is no problem for C’s copy constructor because, c’s C attributes can be copies by C’s copy constructor. But how does the compiler generated D’s copy constructor know the way to copy ‘D’s attributes from C’s object’. This is a conflict which should be avoided.
But, if you provide some sort of ‘weird copy constructor’ (you may need to a default constructor as well) like;
D(const C & c):C(c){}
Then,
calling D d_from_c(c);
is valid. Because, now we have explicitly provided a matching ‘copy’ constructor.
So, saying ‘Inheriting copy constructors are now allowed’ is invalid.

In relation to the paragraph 12.7p3 in the C++ Standard, I have the following questions

What are the specific problems that are being avoided with the restrictions imposed by this clause 12.7p3 (see the first part of paragraph below)?
In the example shown in 12.7p3 (see below) why X(this) is considered defined? Is it because X is not in the path E C D B A ?
struct A { };
struct B : virtual A { };
struct C : B { };
struct D : virtual A { D(A*); };
struct X { X(A*); };
struct E : C, D, X {
E() : D(this), // undefined: upcast from E* to A*
// might use path E* - D* - A*
// but D is not constructed
// D((C*)this), // defined:
// E* - C* defined because E() has started
// and C* - A* defined because
// C fully constructed
X(this) { // defined: upon construction of X,
// C/B/D/A sublattice is fully constructed
}
};
Please find below the start of paragraph 12.7p3:
To explicitly or implicitly convert a pointer (a glvalue) referring to
an object of class X to a pointer (reference) to a direct or indirect
base class B of X, the construction of X and the construction of all
of its direct or indirect bases that directly or indirectly derive
from B shall have started and the destruction of these classes shall
not have completed, otherwise the conversion results in undefined
behavior.
Is it correct to say that the set of all direct and indirect bases of X, mentioned above don't include B, and because of this the code below is well defined, notwithstanding the fact that Base is a direct base of Derived and has yet not started?
struct Base{ Base(Base*); };
struct Derived : Base {
Derived() : Base(this) {};
};
What are the specific problems that are being avoided with the restrictions imposed by this clause 12.7p3 (see the first part of paragraph below)?
The problems have to do with the fact that, at some point, the virtual pointers (to virtual table or virtual base classes) must be initialized for the derived class. This is an initialization that occurs during the "initialization list" (i.e., before the constructor-body begins), usually right after the base class was constructed (e.g., after base class B is constructed, the virtual table pointer and possibly virtual base pointer is set for class X, and then the data members are initialized, and then the constructor's body begins). In general, doing an up-cast before those pointers are initialized will yield undefined behaviour, either because the pointer lookup required cannot be done for a virtual base, or the virtual table pointer will not be set correctly for the virtual functions to be called correctly.
In the example shown in 12.7p3 (see below) why X(this) is considered defined? Is it because X is not in the path E C D B A ?
Because the base class constructors are called in the order that they appear in the class declaration (i.e., struct E : C, D, X {). Because the base classes C and D are both constructed, their common virtual base class A is certainly also constructed. And because X does not inherit from class A, it means that there is a fully-constructed and unambiguous path when performing the cast from class E to class A at this point. That is why that line is well-defined.
Is it correct to say that the set of all direct and indirect bases of X, mentioned above don't include B, and because of this the code below is well defined, notwithstanding the fact that Base is a direct base of Derived and has yet not started?
I'm not sure I follow your explanation, but I can tell you that the code you showed is not well-defined. When calling the constructor of Base by casting Derived* this to Base*, the base-class object in the derived class is not yet constructed, and thus, the cast is undefined.
The only way that this code could make sense is if there were no virtual functions or virtual base classes involved in the hierarchy, in which case, such code would not be needed since the this pointer to the base class is already sent, implicitly, to the base-class constructor.