Reputation: 4435
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.
Upvotes: 0
Views: 71
Reputation: 47650
You may create one variadic template catch-all constructor that will pass arguments to Base and construct Printer as needed, like this
class Derived: ... {
template<typename... Args>
Derived(Args&&... args): Base(std::forward<Args>(args)...), Printer(*this) {}
}
Upvotes: 1
Reputation: 4435
I've found an amazingly simple solution. Let's store in Printer
a pointer to T
instead of a reference.
template<typename T>
class Printer {
public:
const T* objectPtr;
Printer() : objectPtr(nullptr) {}
Printer(const T& object) : objectPtr(&object) {}
void print() {
if (objectPtr) {
cout << *objectPtr << endl;
} else {
cout << static_cast<const T&>(*this) << endl;
}
}
};
It looks not really safe but making Printer()
ctor private seems to make the deal.
Upvotes: 0
Reputation: 16431
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;
}
};
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;
}
};
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;
};
Upvotes: 1
Reputation: 597875
Now I have a problem: Base constructors know nothing about Printer and thus cannot initialize it in any way.
To do what you are attemping, you won't be able to use a using Base::Base
statement anymore. You will have to be more explicit about the constructors that Derived
implements, so they can each initialize the Printer
base class as needed, eg:
class Derived : public Base, public Printer<Derived> {
public:
typedef Printer<Derived> MyPrinter;
Derived() : Base(0), MyPrinter(*this) {}
Derived(int x) : Base(x), MyPrinter(*this) {}
};
Or:
class Derived : public Base, public Printer<Derived> {
public:
typedef Printer<Derived> MyPrinter;
Derived() : Derived(0) {}
Derived(int x) : Base(x), MyPrinter(*this) {}
};
Upvotes: 1