Benniczek
Benniczek

Reputation: 1

Calling constructor of non-template base class through template class constructor in initialization list

I've come across a problem in a class hierarchy I couldn't solve so far. Below you get the minimal example where the template Base class itself inherits from another class that is not a template (called AbsoluteBase in the example below). The class Derived then inherits from Base with its template argument also being Bases' template argument:

#include <iostream> 
using namespace std;

class AbsolutBase {
    protected:
        int number;

        AbsoluteBase(int _number) {
            number = _number;
        }
        virtual ~AbsoluteBase() {}
        virtual void print() const = 0;
};

template <typename T> class Base : virtual public AbsoluteBase {
    public:
        Base(int _number) : AbsoluteBase(_number) {}

        virtual void print() const {
            cout << number << endl;
        }
};

template <typename T> class Derived : public Base<T> {
    public:
       Derived(int _number) : Base<T>::Base(_number) {}
};

int main() {
    Derived<char> object(100);
    object.print();
}

So each constructor calls the constructor of its parent and passes an integer as argument all the way down to AbsoluteBase. But when compiling the code I get:

error: no matching function for call to 'AbsoluteBase::AbsoluteBase()'
note: candidates are: AbsoluteBase::AbsoluteBase(int)
note: candidate expects 1 argument, 0 provided

Making an instance of Base works just fine but when calling its constructor in the initialization list of Derived, the compiler wants AbsolutBase() as constructor even though the integer argument is given. Obviously, when defining a default constructor in AbsoluteBase the print() function outputs garbage as no value has been passed to number.

So something has to be wrong with my call of Base<T>::Base(int) but I can't see what it is. I am grateful for every explanation and help!

Greetings, Benniczek

Upvotes: 0

Views: 1172

Answers (3)

AbsoluteBase is a virtual base class. As such, it must be initialized by the constructor of the most-derived class. Your initializer AbsoluteBase(_number) is valid, but it is only used if you instantiate an object of type Base<T> directly.

The best solution is probably not to make AbsoluteBase a virtual base class.

The reason for this rule is:

class Silly: public Base<int>, Base<long>
{
public:  
    Silly() : Base<int>::Base(1), Base<long>::Base(2) {}
};

There is only one AbsoluteDerived object (that's what virtual means in this context), so is it initialized with 1 or 2?

Upvotes: 1

RyanP
RyanP

Reputation: 1918

You declared Absolute Base as class AbsolutBase without the e. Compiles just fine with the typo fixed.

Edit: It also will not compile if you have class base doing virtual inheritance from Absolute. Unless you need virtual inheritance (using multiple inheritance?) you can declare it class Base : public AbsoluteBase

If you do need virtual inheritance (the diamond problem) you'll need to initialize AbsoluteBase in your Derived class. Given that Base virtually inherits from AbsoluteBase, Derived could also inherit from a Base2 which also virtually inherits from AbsoluteBase (this is the point of virtual inheritance, one class can inherit from two different classes that themselves inherit from a common base). As it is a virtual inheritance, there can be only 1 AbsoluteBase even though Base and Base2 could inherit from AbsoluteBase, so how does it get initialized? This is why Derived is required to initialize it directly, so that when the 1 copy of AbsoluteBase is made, it is well understood how it will be initialized.

Again, that is messy, if you don't need virtual inheritance (you probably don't I'd guess...) you can just make Base inherit from AbsoluteBase publically and call it a day.

Upvotes: 0

Criss
Criss

Reputation: 611

Because of virtual inheritance. When base class inherits virtually from another class, the child class of base it must also call constructor of virtual parent of its parent. Since you didn't do this, compiler is trying to call no-argument AbsoluteBase's ctor. So you just have to code as follows:

template <typename T> class Derived : public Base<T> {
    public:
       Derived(int _number) : AbsoluteBase(_number), Base<T>::Base(_number) {}
};

Upvotes: 0

Related Questions