This question already has an answer here:
Why is Default constructor called in virtual inheritance?
(1 answer)
Closed 2 years ago.
I have this simple code:
class C0
{
public:
C0(std::string i) : i_(i) {}
private:
std::string i_;
};
class C1 : public virtual C0
{
public:
constexpr static auto const e{"e"};
C1(std::string i = e) : C0(i) {}
};
class C2 : public virtual C1
{
public:
C2(std::string k) : k_(k) {}
private:
std::string k_;
};
Compiled as: [clang++|g++] -std=c++17 example.cpp
I get the following error:
With g++ (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0:
example.cpp: In constructor ‘C2::C2(std::__cxx11::string)’:
example.cpp:23:41: error: no matching function for call to ‘C0::C0()’
C2(std::string k) : k_(k) {}
^
example.cpp:6:17: note: candidate: C0::C0(std::__cxx11::string)
C0(std::string i) : i_(i) {}
^~
example.cpp:6:17: note: candidate expects 1 argument, 0 provided
example.cpp:3:7: note: candidate: C0::C0(const C0&)
class C0
^~
example.cpp:3:7: note: candidate expects 1 argument, 0 provided
example.cpp:3:7: note: candidate: C0::C0(C0&&)
example.cpp:3:7: note: candidate expects 1 argument, 0 provided
With clang++ version 6.0.0-1ubuntu2:
example.cpp:23:17: error: constructor for 'C2' must explicitly initialize the base class 'C0' which does not have a default constructor
C2(std::string k) : k_(k) {}
^
example.cpp:3:7: note: 'C0' declared here
class C0
^
Why does the compiler complain about missing default constructor in class C0 when the default constructor of class C1 explicitly calls C0 constructor passing the argument?
When I define a default constructor in C0, the call to C0(i) from C1 does not happen. So the member variable C0::i_ is not initialised. Shouldn't there be a call to C0(i) rather than the default constructor of C0 when C2 is instantiated?
The most derived class is the one that is responsible for constructing the virtual bases. To fix this, add : C0(k) to C2.
Related
I'm sorry if this feels like a cheap sequel to my last question.
I have a diamond inheritance where D is derived from both B and C, who in turn are both derived (virtually) from A. A, B and C are abstract, and thanks to the answers to my previous questions the compiler is now aware of it and all is fine.
Now, I need to create a class E derived from D. As far as I know, normally the constructor E::E should call D::D, and it would be D::D's job to call all of A::A, B::B, and C::C.
But my compiler really insists on having E::E call A::A itself.
Here is a simple example I made:
class A { //abstract
protected:
A(int foo) {}
virtual void f() =0;
};
class B: public virtual A { // abstract
protected:
B() {}
};
class C: public virtual A { // abstract
protected:
C() {}
};
class D: public B, public C { // concrete
public:
D(int foo, int bar) :A(foo) {}
void f() {}
};
class E: public D { // concrete
public:
E(int foo, int bar, int buz) :D(foo, bar) {}
};
int main()
{
return 0;
}
And here is the compilation error:
$ g++ test.cpp
test.cpp: In constructor ‘E::E(int, int, int)’:
test.cpp:25:49: error: no matching function for call to ‘A::A()’
25 | E(int foo, int bar, int buz) :D(foo, bar) {}
| ^
test.cpp:3:9: note: candidate: ‘A::A(int)’
3 | A(int foo) {}
| ^
test.cpp:3:9: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr A::A(const A&)’
1 | class A { //abstract
| ^
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr A::A(A&&)’
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
I know the virtual inheritance is correct and I know the compiler knows which classes I intend to be abstract and which I intend to be instantiable, because if I remove class E, the code compiles.
What am I missing?
But my compiler really insists on having E::E call A::A itself.
Like I explained in anwer to your previous question: "the constructor of the most derived class calls the constructor of the virtual base".
All non-abstract classes in a hierarchy that contains virtual bases must correctly initialise the virtual bases because they can potentially be instantiated as the most derived class. For example, if you create an instance of E, then it is the constructor of E that initialises the virtual base A.
In your code, the constructor of E attempts to use the default constructor of A by omitting the initialiser. But A is not default constructible, so the program is ill-formed.
What am I missing?
The initialiser for the virtual base A in the constructor of E.
As far as I know, there is no way to delegate the construction to of the virtual base to another concrete base such as D.
My understanding, for instance reading this, is that the constructor of a derived class does not call its virtual base class' constructor.
Here is a simple example I made:
class A {
protected:
A(int foo) {}
};
class B: public virtual A {
protected:
B() {}
};
class C: public virtual A {
protected:
C() {}
};
class D: public B, public C {
public:
D(int foo, int bar) :A(foo) {}
};
int main()
{
return 0;
}
For some reason, the constructors B::B() and C::C() are trying to initialize A (which, again in my understanding, should have already been initialized by D at this point):
$ g++ --version
g++ (GCC) 10.2.0
Copyright (C) 2020 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.
$ g++ test.cpp
test.cpp: In constructor ‘B::B()’:
test.cpp:8:13: error: no matching function for call to ‘A::A()’
8 | B() {}
| ^
test.cpp:3:9: note: candidate: ‘A::A(int)’
3 | A(int foo) {}
| ^
test.cpp:3:9: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr A::A(const A&)’
1 | class A {
| ^
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr A::A(A&&)’
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp: In constructor ‘C::C()’:
test.cpp:13:13: error: no matching function for call to ‘A::A()’
13 | C() {}
| ^
test.cpp:3:9: note: candidate: ‘A::A(int)’
3 | A(int foo) {}
| ^
test.cpp:3:9: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr A::A(const A&)’
1 | class A {
| ^
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
test.cpp:1:7: note: candidate: ‘constexpr A::A(A&&)’
test.cpp:1:7: note: candidate expects 1 argument, 0 provided
I'm certain there is something very basic I misunderstood or am doing wrong, but I can't figure what.
The constructor of virtual base is constructed. It is constructed conditionally. That is, the constructor of the most derived class calls the constructor of the virtual base. If - this is the condition - the derived class with virtual base is not the concrete class of the constructed object, then it will not construct the virtual base because it has already been constructed by the concrete class. But otherwise it will construct the virtual base.
So, you must correctly initialise the virtual base class in constructors of all derived classes. You simply must know that specific initialisation doesn't necessarily happen in case the concrete class is not the one which you are writing. The compiler doesn't and cannot know whether you will ever create direct instances of those intermediate classes, so it cannot simply ignore their broken constructors.
If you made those intermediate classes abstract, then the compiler would know that they are never the most concrete type and thus their constructor would not be required to initialise the virtual base.
For some reason, the constructors B::B() and C::C() are trying to initialize A (which, again in my understanding, should have already been initialized by D at this point):
But what should compiler do if somebody constructs C solo? The final object D will call the constructor of A but you define constructor to C which implies that it can be constructed but the constructor is faulty cause it cannot construct A.
Putting aside more complex class hierarchies, for any derived type there is exactly one copy of its virtual base. The rule is that the constructor for the most-derived type constructs that base. The compiler has to generate code to handle the bookkeeping for that:
struct B { };
struct I1 : virtual B { };
struct I2 : virtual B { };
struct D : I1, I2 { };
B b; // `B` constructor initializes `B`
I1 i1; // `I1` constructor initializes `B` subobject
I2 i2; // `I2` constructor initializes `B` subobject
So far, it's easy enough to picture, since the initialization is done the same way as it would be if B was not a virtual base.
But then you do this:
D d; // which constructor initializes `B` subobject?
If the base wasn't virtual, the I1 constructor would initialize its B subject, and the I2 constructor would initialize its B subobject. But because it's virtual, there's only one B object. So which constructor should initialize it? The language says that the D constructor is responsible for that.
And the next complication:
struct D1 : D { };
D1 d1; // `D1` constructor initializes `B` subobject
So, along the way we've created five different objects, each with a virtual base of type B, and each with the B subobject being constructed from a different constructor.
Putting the responsibility on the most-derived type makes the initialization easy to understand and to visualize. There could have been other rules, but this one really is the simplest.
In the below code, I initialize const member of Base class in the most derived class Grandchild.
class Base {
public:
Base(int x_) : x(x_) {}
private:
const int x;
};
class Child : public virtual Base {
public:
virtual ~Child() = 0;
};
class Grandchild : public virtual Child {
public:
Grandchild() : Base(42) {}
};
Child::~Child() {}
int main() {
Grandchild gc;
}
In case of virtual inheritance, the Base class constructor is called by the most derived class. Hence, I expect the code to compile successfully.
clang 4.0 compiles it successfully, whereas gcc 4.9.2 emits the following error:
In constructor 'Grandchild::Grandchild()':
16:27: error: use of deleted function 'Child::Child()'
9:7: note: 'Child::Child()' is implicitly deleted because the default definition would be ill-formed:
9:7: error: no matching function for call to 'Base::Base()'
9:7: note: candidates are: 3:5: note: Base::Base(int)
3:5: note: candidate expects 1 argument, 0 provided
1:7: note: constexpr Base::Base(const Base&)
1:7: note: candidate expects 1 argument, 0 provided
1:7: note: constexpr Base::Base(Base&&)
1:7: note: candidate expects 1 argument, 0 provided
What does the standard say about this?
It seems there was a change in the C++ standard clarifying the requirements of generated constructors for virtual base classes. See CWG257. As far as I understand this text your situation should be allowed. Prior to the change the situation was unclear.
This change was voted into the Working Paper in October 2009, i.e., it should be applicable to compiling with C++11.
I am trying to compile a code that involves inheritance.
#include "MapEntityClass.h"
class RectangularEntityClass:public MapEntityClass
{
public:
void drawOnMap(MapClass *mapObj) const;
protected:
};
The parent class is MapEntityClass, which does not have a default constructor, but has a value constructor. When I compile, I get the following error:
RectangularEntityClass.h: In constructor ‘RectangularEntityClass::RectangularEntityClass()’:
RectangularEntityClass.h:12:7: error: no matching function for call to ‘MapEntityClass::MapEntityClass()’
class RectangularEntityClass:public MapEntityClass
^
RectangularEntityClass.h:12:7: note: candidates are:
In file included from main.cpp:1:0:
MapEntityClass.h:32:5: note: MapEntityClass::MapEntityClass(const PixelLocationClass&, const ColorClass&)
MapEntityClass(
^
MapEntityClass.h:32:5: note: candidate expects 2 arguments, 0 provided
Any idea what is wrong?
In inheritance, the subclass need not have a constructor only if parent class doesn't have a constructor or only default constructor.
In any case, if parent class happens to have a parameterized constructor, the subclass should have a parameterized constructor which should invoke the parent class constructor.
Example:
class A {
int aVal;
public:
A(int);
};
A::A(int aVal)
{
this->aVal = aVal;
}
class B : public A {
int bVal;
public:
B(int, int)
};
B::B(int aVal, int bVal) : A(aVal)
{
this->bVal = bVal;
}
I'm trying to define some classes but I get some errors I'm not being able to decode.
I do not understand why it states I'm using Test's constructor when I am not.
test.cpp: In constructor 'Test2::Test2(int)':
test.cpp:12:34: error: use of deleted function 'Test::Test()'
explicit Test2(const int line) {}
^
test.cpp:3:7: note: 'Test::Test()' is implicitly deleted because the default definition would be ill-formed:
class Test : public std::runtime_error {
^
test.cpp:3:7: error: no matching function for call to 'std::runtime_error::runtime_error()'
test.cpp:3:7: note: candidates are:
In file included from test.cpp:1:0:
C:/Development/MinGW/x86_64-w64-mingw32/include/c++/stdexcept:119:5: note: std::runtime_error::runtime_error(const string&)
runtime_error(const string& __arg);
^
C:/Development/MinGW/x86_64-w64-mingw32/include/c++/stdexcept:119:5: note: candidate expects 1 argument, 0 provided
C:/Development/MinGW/x86_64-w64-mingw32/include/c++/stdexcept:112:9: note: std::runtime_error::runtime_error(const std::runtime_error&)
class runtime_error : public exception
^
C:/Development/MinGW/x86_64-w64-mingw32/include/c++/stdexcept:112:9: note: candidate expects 1 argument, 0 provided
Minimal working example (gcc 4.9.1, g++ -std=c++11):
#include <stdexcept>
class Test : public std::runtime_error {
public:
virtual ~Test() noexcept {}
virtual const char * what() const noexcept = 0;
};
class Test2 : public Test {
public:
explicit Test2(const int line) {}
virtual const char * what() const noexcept { return ""; }
};
Your constructor in the class Test2 does not pass any arguments to the base class. So the default constructor of the base class - which is Test - is used. And the same story with Test then - its default constructor tries to call the default constructor of its base class - std::runtime_error - which does not exist. Hence the error. You need to pass message string argument to std::runtime_error, e.g.:
Test() : std::runtime_error("some message") {}