3

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?

C. Seidel
  • 45
  • 6
  • If your constructor is `private`, `class B : A, ..` cannot compile to construct `A`. if it is protected, `B` no longer requires to use smartPointer, and so A (inherited) is not in a smartPointer... – Jarod42 Mar 23 '16 at 22:04
  • is there any chance to build something i want? to enforce smart pointer use? – C. Seidel Mar 23 '16 at 22:18

3 Answers3

3

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(); 
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • Why must Builder inherit from T? In A and B, Builder must be declared as friend, is this correct? – C. Seidel Mar 24 '16 at 00:15
  • @C.Seidel Builder is a class n so no need to declare it as friend. Builder inherits from T which is the template type (i.e. it will be after type deduction either A or B): this give it access to protected members. Here an [online demo](http://ideone.com/Bfou25) – Christophe Mar 24 '16 at 00:40
  • Your posted demo works fine with std::shared_ptr. But when substitute with QSharedPointer, the compiler complains about the private constructor. – C. Seidel Mar 24 '16 at 01:02
  • Curiosly, for me, this wont compile (even when QT is avaible) http://ideone.com/Ls6UfD with error: calling a protected constructor of class 'A' return QSharedPointer( new T(std::forward(all)...) ); – C. Seidel Mar 24 '16 at 01:27
  • The idea of gaining access via inheritance is clever, but not something I would put in code. The inheritance communicates an Is-A relationship that doesn't exist, i.e. at first glance it lies to a reader. In contrast a `friend` declaration in each relevant class tells a reader of the code how instantiate it. – Cheers and hth. - Alf Mar 24 '16 at 02:50
  • @Christophe: *"both overloads differs only by the return type. This is not allowed"* Not really, it would be ambiguous to have same name in both bases, but you can specify which one to call: [Demo](http://coliru.stacked-crooked.com/a/ea6f830427db122b) – Jarod42 Mar 24 '16 at 08:52
  • @Jarod42 Maybe I expressed myself in an ambiguous manner: I was referring to the overload resolution in the context of template argument deduction. As according to 14.8.2.1 template arguments are deduced based on the function's parameter (and not its return type), so that here, at the end 2 candidate remain. – Christophe Mar 24 '16 at 10:59
  • @Christophe: Even with different arguments, it would be ambiguous, as they have the same name from different base: [Demo](http://coliru.stacked-crooked.com/a/19c4c7407dfa228e). – Jarod42 Mar 24 '16 at 11:05
  • @Jarod42 ok, so idea of smuggle in an additional parameter wouldn't help anyway. – Christophe Mar 24 '16 at 11:35
2

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.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
0

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.

Daniel Kamil Kozar
  • 18,476
  • 5
  • 50
  • 64
skypjack
  • 49,335
  • 19
  • 95
  • 187
  • But i would still need to write nearly the same create method in all inherited classes. Or am i wrong? – C. Seidel Mar 23 '16 at 22:39
  • You can define it as a template class, indeed. This should solve the problem of defining the create method all around. – skypjack Mar 23 '16 at 22:42