Reputation: 45
I want to make sure only smart pointers are build from my classes so i made all constructors protected.
To create objects i build this "Buildable"-Policy:
template <typename T>
class Buildable
{
public:
template<typename ...Args>
static QSharedPointer<T> buildObject(Args&&... all)
{
return QSharedPointer<T>( new T(std::forward<Args>(all)...) );
}
};
If i use this this policy in a base class, all goes well. But when i use it in base and a derived class like this:
class A : public Buildable<A> {}
class B : A, public Buildable<B>{}
the compiler argues: Error: member 'buildObject' found in multiple base classes of different types
I don't know how to solve this problem. Any ideas?
Upvotes: 3
Views: 204
Reputation: 73490
Cause of the error
This is because the multiple inheritance here:
class B : A, public Buildable<B>{};
It causes class B
to inherit from Buildable<A>
and from Buildable<B>
which both contain a buildObject()
overload. Unfortunately, both overloads differs only by the return type. This is not allowed.
Design issue
Unfortunately, you can't avoid this unless you could smuggle an additional parameter to buildObject()
, which could permit the compiler to use proper type derivation to resolve avoid ambiguity.
But do you really intend to have multiple inheritance in your design here ? If your classes would be polymorphic, couldn't you use a class B defined as follows:
class B : public A {}; // inherits from Buildable A
...
QSharedPointer<A> b = B::buildObject(); //
Alternative design
The alternative could be to put the building class at the bottom of the derivation, to avoid the conflicts. Make your constructors for classes A and B protected, and use this template class :
// (I used here shared_ptr instead of QSharedPointer for testing purpose)
template <typename T>
class Builder : public T
{
public:
Builder() = delete;
Builder (const Builder&) = delete;
template<typename ...Args>
static shared_ptr<T> buildObject(Args&&... all)
{
return make_shared<T>(std::forward<Args>(all)...) ;
}
};
class A { };
class B : public A {}; // Normal class hierarchy.
// can't be instantiated if ctor are protected.
shared_ptr<A> a = Builder<A>::buildObject();
shared_ptr<B> b = Builder<B>::buildObject();
Upvotes: 3
Reputation: 145389
One easy solution to the design problem is to make buildObject
a freestanding function template, that you make a friend
of each user class.
In order to simplify friend
declarations you may want to put that function template in a class.
Then it's very much like your existing code, except there's no inheritance from the factory class:
#include <memory> // std::shared_ptr
#include <utility> // std::forward
namespace my{
using std::forward;
using std::shared_ptr;
template< class Class >
struct Make_
{
template< class... Args >
static auto instance( Args&&... args )
-> shared_ptr<Class>
{ return shared_ptr<Class>( new Class( forward<Args>( args )... ) ); }
};
class A
{
template< class > friend struct Make_;
protected:
A( int ) {}
};
class B
: public A
{
template< class > friend struct Make_;
protected:
B( int x ): A( x ) {}
};
} // namespace my
auto main() -> int
{
using namespace my;
auto p = Make_<B>::instance( 42 );
}
General solutions to the immediate technical problem of providing a covariant function without repeating its definition, include:
A macro (that expands to the function definition).
This is what I recommend, if you feel that it absolutely has to be a member function. It's one of the few legitimate uses of macros.
Middle-man inheritance.
Essentially instead of inheriting directly from a Base
and also from a mixin, you inherit from the mixin and ask it to inherit from Base
. You need to forward constructor arguments from the mixin.
Dominance in virtual inheritance hierarchy.
A really ugly and complex solution. Don't go there. But it's a technical possibility.
Finally there is non-solution of using a function that returns a (smart) pointer to base class. Where the client code has to cast it down. It's really ungood in its own way, but I mention it for completeness.
Upvotes: 2
Reputation: 50550
You could inherit from QEnableSharedFromThis
(see here for further details).
As an example:
class C: public QEnableSharedFromThis<C> {
C() = default;
// all the other constructors
public:
template<typename ...Args>
static QSharedPointer<C> create(Args&&... all) {
// refers one of the available constructors
return QSharedPointer<C>(new C{std::forward<Args>(all)...});
}
QSharedPointer<C> getSharedFromThis() {
return sharedFromThis();
}
};
You can use it as a base class for your hierarchy.
Upvotes: 0