Ivan Smirnov Ivan Smirnov - 1 month ago 11
C++ Question

Setting a default constructor for one type with multiple inheritance

I have two classes,

Base
and
Derived
.
Derived
inherits all
Base
's constructors. Also, I have a template class
Printer<T>
which holds a reference to an object of type
T
and has a method
print()
which prints an object somehow. Here is a minimal illustration.

class Base {
public:
Base(int x) : x(x) {}
int x;
};

template<typename T>
class Printer {
public:
const T& object;

Printer(const T& object) : object(object) {}

void print() {
cout << object << endl;
}
};

class Derived : public Base {
public:
using Base::Base;
};

std::ostream& operator<<(std::ostream& out, const Derived& d) {
return out << d.x;
}

int main() {
Derived d(1);

Printer<Derived>(d).print();
}


Now I'd like to avoid direct usage of
Printer
and allow such syntax:
Derived d(1); d.print();
. Thus I tried to inherit
Derived
also from
Printer<Derived>
.

class Derived : public Base, public Printer<Derived> {
public:
typedef Printer<Derived> MyPrinter;

using Base::Base;

Derived() : MyPrinter(*this) {}
};


Now I have a problem:
Base
constructors know nothing about
Printer
and thus cannot initialize it in any way. I also cannot use a constructor delegation here because the constructor which is used in
Derived
is actually inherited from
Base
.

Can I somehow make the default constructor of
Derived
be delegated by any other constructor, even inherited ones? Or maybe there is some other patterns to initialize the second base in multiple inheritance?

One more thing which hardens everything is that I don't have access to
Base
's code and only can use it as is.

UPDATE
On Remy Lebeau's answer:
Base
can have multiple constructors which I'm not aware of (it is a template class as well), so I can't implement all of them and must use
using Base::Base
idiom.

On krzaq's answer:
Printer
actually also has many methods, not only
print()
, so implementing a forwarder class is a nuisance and I try to avoid it.

Answer

If all you need is to have access to Derived instance from Printer<Derived> then you can simply cast it down:

template<typename T>
class Printer {
public:
    const T& object;

    Printer() : object(static_cast<T&>(*this)) {}

    void print() {
        cout << object << endl;
    }
};

live demo

or make away with the reference altogether and make your class eligible for EBO:

template<typename T>
class Printer {
public:
    void print() {
        cout << static_cast<T&>(*this) << endl;
    }
};

live demo

If you can't/don't want to touch Printer either, I'd create a separate template PrinterForwarder to firward the print() call to the right printer:

template<typename T>
class PrinterForwarder
{
public:
    void print() {
        Printer<T>(static_cast<T&>(*this)).print();
    }
};

class Derived : public Base, public PrinterForwarder<Derived> {
public:
    using Base::Base;
};

live demo