Not to use default constructor for virtual base class - c++

I was looking at Understanding virtual base classes and constructor calls and I understand that the most derived class will call the top-base class default constructor directly. But is there a way not to call top-base default constructor?
One example for my problem,
#include <iostream>
#include <cstdint>
////// BEGIN LIBRARY CODE
class A
{
public:
A()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
A(int)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
class B : virtual public A
{
public:
B()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
class C: virtual public A
{
public:
C()
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
class D: public B, public C
{
public:
D(int x) : A(x), B(), C() // ok. works as expected
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
////// END LIBRARY CODE
////// USER CODE BEGINS
class E: public D
{
public:
E() : D(42) // problem, invokes A(), not A(int)
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
E(int x) : D(x) // same problem
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
};
////// USER CODE ENDS
int main()
{
D d(1);
E e1,e2(42);
}
Output
A::A(int)
B::B()
C::C()
D::D(int)
A::A()
B::B()
C::C()
D::D(int)
E::E()
A::A()
B::B()
C::C()
D::D(int)
E::E(int)
Problem:
I want E to only care about D construction. But from the explanation in the beginning, if I don't add A::A(int) below, I will always use default constructor no matter how I update class D
E() : A(42), D(42) // works, but undesirable
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
E(int x) : A(x), D(x) // this works, but undesirable
{
std::cout << __PRETTY_FUNCTION__ << '\n';
}
In short, I don't want the user-code class E extending the library code class D to have to specify class A construction parameters so...
Is there any way it can be done by a "middle" class in the inheritance chain such as class D in my example above, to alleviate the most-derived classes from having to do so?

The constructor for the top-level class calls the constructors for all virtual bases. But that's no different from calling constructors for direct non-virtual bases: if you put in an explicit constructor call, that's what the compiler will use; if you don't, it will use the default constructor. So the answer is no, if the default constructor isn't appropriate, you can't avoid calling A's constructor from E.

Related

Multiple and virtual inheritance C++

Full disclaimer, this is homework - not graded, just given to students so we can practice.
I'm asking for help, because we won't get an answer and I just want to know how to solve it.
What I can do is define structures B and C. Their interface has to be "like A's interface, with modifications so it works correctly".
I can't add any new methods. I also can't change anything in struct A.
This is the code:
#include <iostream>
struct A;
struct B;
struct C;
struct A {
A() {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
int main(){
C c;
}
And the desired output is:
A::A()
A::A()
B::B()
A::A()
A::A()
B::B()
A::A()
C::C()
What I tried to do so far:
First I started like this, just to check things out:
struct B : public A {
B() : A() {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
struct C : public B {
C() : B() {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
Which gave me:
A::A()
B::B()
C::C()
So I tried to get first C, then A, then B, then A (desired output from the bottom):
struct B : public virtual A {
B() : A() {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
struct C : public B, public A {
C() : A(), B() {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
But of course this didn't work. (warning: direct base ā€˜Aā€™ inaccessible in ā€˜Cā€™ due to ambiguity)
Adding virtual keyword like below gave me again C, B then A, from the bottom:
struct B : public virtual A {
B() : A() {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
struct C : public virtual A, public B {
C() : A(), B() {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
If I want to get C, then A I have to do the following (but then there will be no B)
struct C : public virtual A {
C() : A() {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
struct B : public virtual A, public C {
B() : C(), A() {
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
I also have no idea how could I get A::A() twice in a row.
I just want to understand how this should work, but if you still feel like you don't want to help me with a solution, then please leave me some tips.
I do not see a restriction in the exercise on use of class members:
struct A
{
A() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};
struct B : A
{
B() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
};
struct C
: B
, A
{
C() { std::cout << __PRETTY_FUNCTION__ << "\n"; }
B b;
A a;
};
UPDATE
The desired output had changed since my first answer was posted. So previous answer had become wrong. But now you see the point - you can use composition and copy A a; to B definition and remove A inheritance from C definition.
You can do something like:
struct B : A { //inherit from A
B() {
A();
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
struct C : B { //inherit from B
C() {
B(); //anonymous B object
A(); //anonymous A object
std::cout << __PRETTY_FUNCTION__ << "\n";
}
};
Running sample

Explicitly defaulted destructor disables default move constructor in a class

I have run into a problem that a move constructor of a superclass did not get invoked properly when its subclass has an explicitly defaulted destructor. The move constructor does get invoked when the destructor is implicitly defaulted (not provided at all in the supclass definition).
I am aware of the constraints that the compilers should apply to default move constructors. Yet, I have been by all means sure that the compiler should not discriminate between explicitly/implicitly defaulted destructors (or constructors as well) when applying these rules. In other words, explicitly defaulted destructor should not be treated as user-defined one (in contrast to an empty user-defined destructor ).
Tested with MSVC 2019 only.
Am I or MSVC right here?
#include <iostream>
class A {
public:
A() = default;
A(const A&) { std::cout << "Auch, they'r making copy of me(?!)" << std::endl; }
A(A&&) { std::cout << "I am moving :)" << std::endl; }
~A() = default;
};
class B : public A {
public:
};
class C : public A {
public:
C() = default;
};
class D : public A {
public:
~D() = default;
};
class E : public A {
public:
E() = default;
E(const E&) = default;
E(E&&) = default;
~E() = default;
};
int main()
{
std::cout << "\n---- A ----\n" << std::endl;
A a;
A a2(std::move(a));
std::cout << "\n---- B ----\n" << std::endl;
B b;
B b2(std::move(b));
std::cout << "\n---- C ----\n" << std::endl;
C c;
C c2(std::move(c));
std::cout << "\n---- D ----\n" << std::endl;
D d;
D d2(std::move(d));
std::cout << "\n---- E ----\n" << std::endl;
E e;
E e2(std::move(e));
}
EXPECTED: Display "I am moving :)" in all cases
ACTUAL : Displays "Auch, they'r making copy of me(?!)" in case D
When you declare a defaulted destructor in D, you disable the compiler-generated move constructor and move assignment operator in D (!), not the base class version. This is why you get the expected output with E, where you override the defaulted operation by the compiler by explicitly = defaulting the special member functions. The compiler-generated move constructor does the right thing for movable base class types, so follow the rule of five and = default the special member functions for D.
Have a look at the table in this answer. It's a very useful reference to be kept under the pillow.

Ctor-initializer for a class doesn't call to the appropriate constructor

I'm playing with construting/destructing object. Here is what I've tried http://coliru.stacked-crooked.com/a/ff17cc5649897430:
#include <iostream>
struct B{
B(){ std::cout << "B()" << std::endl; }
B(int){ std::cout << "B(int)" << std::endl; }
};
struct A : virtual B
{
int B;
A(int a) : B(a) { std::cout << "A(int)" << std::endl; }
} a(10);
int main()
{
}
The program output is
B()
A(int)
Why? I explicitly specify the constructor of the class B to be invoked in the ctor-initializer.
The B(a) is constructing the B member variable. Name your variables better and you'll see what you want to see.

Virtual base class initialization

Looking for an explanation of some fact in the standard I found this:
An abstract class (10.4) is never a most derived class, thus its
constructors never initialize virtual base classes, therefore the
corresponding mem-initializers may be omitted.
This is from the paragraph 12.6.2 of the final working draft. Since, It's embedded in Note and no example is provided, I don't know how it should be treated. So, I tried the example:
#include <iostream>
struct B{
~B(){}
B(){ std::cout << "B()" << std::endl; }
B(int){ std::cout << "B(int)" << std::endl; }
};
struct A : virtual B
{
int s;
virtual void foo() = 0;
A(int a) : B(a) { std::cout << "A(int)" << std::endl; }
};
struct D : A{
D() : A(10){ }
virtual void foo(){ }
} d;
int main()
{
}
and got the output
B()
A(int)
http://coliru.stacked-crooked.com/a/b16c68226f072ced
in spite of specifying the B's constructor in the ctor-initializer. Is that what they mean?
In this example, D is the most derived class and will construct B using the default (no parameter) constructor.
An abstract class is never most derived because you have to derive from it in order to instantiate it.

Inheritance: No appropriate default constructor available

I get the error: No appropriate default constructor for B. However, I don't understand why the compiler wants to call a default constructor, when I give the arguments ii and DONT want to call the default.
#include <iostream>
using namespace std;
class A {
int i;
public:
A(int ii) { i = ii; cout << "Constructor for A\n"; }
~A() { cout << "Destructor for A\n"; }
void f() const{}
};
class B {
int i;
public:
B(int ii) { i = ii; cout << "Constructor for B\n"; }
~B() { cout << "Destructor for B\n"; }
void f() const{}
};
class C:public B {
A a;
public:
C() { cout << "Constructor for C\n"; }
~C() { cout << "Destructor for C\n"; }
void f() const {
a.f();
B::f();
}
};
class D:public B {
C c;
public:
D(int ii) { B(ii); cout << "Constructor for D\n"; }
~D() { cout << "Destructor for D\n"; }
};
int main() {
D d(47);
}
Your parent constructor should be called in the initializer list:
class D:public B {
C c;
public:
D(int ii) : B(ii)/* <- */ { cout << "Constructor for D\n"; }
~D() { cout << "Destructor for D\n"; }
};
Note the /* <- */ comment. That needs to be changed.
What you are doing right now is to create an instance of B() in you class D constructor, which is not being used:
D(int ii) { B(ii); /* <- useless*/ }
D(int ii) { B(ii); cout << "Constructor for D\n"; }
Calls the default constructor of B. The B(ii) creates an temporary object of B which gets destructed as soon as constructor of D returns, In short it does not call the constructor for Base class of object which is being constructed.
Solution:
To be able to call a particular constructor of your Base class you should use Member Initializer list.
D(int ii) : B(ii)
{
}
This code:
class C:public B
{
C() { cout << "Constructor for C\n"; }
};
attempts to call B's default constructor.
You might want:
class C:public B
{
C() : B(0) { cout << "Constructor for C\n"; }
};
but that depends on your logic.
The following is also wrong:
D(int ii) { B(ii); cout << "Constructor for D\n"; }
it should be
D(int ii) : B(ii) { cout << "Constructor for D\n"; }
Calling the base class constructor in the body of the child class constructor merely creates a temporary object which doesn't do anything. To get the behavior you expect, you must call the constructor in the initializer list.
You're creating a D, which is derived from B -- but D's ctor doesn't pass a parameter to B's constructor, which would require that B have a default ctor.
To fix this, you typically need to write D to provide a parameter to B's ctor:
class D : public B {
C C;
public:
D(int ii) : B(ii) { cout << "ctor for D\n"; }
};
You need to realize that base and member subobjects are constructed by the time you enter the body of your constructor! That is, if you have a base or a member which doesn't have a default you need to pass its argument in the member initializer list:
D(int ii): B(ii) { std::cout << "constructor for D\n"; }
The object you constructed in your body of the D constructor is just a temporary object which doesn't really serve any purpose in your case (temporary object may be useful in some cases, though).