C. Seidel
C. Seidel

Reputation: 45

Make sure only Smartpointers are build

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

Answers (3)

Christophe
Christophe

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

Cheers and hth. - Alf
Cheers and hth. - Alf

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

skypjack
skypjack

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

Related Questions