1

I'm using a template class with CRTP to implement the clone pattern, with a second template parameter Base to allow for multiple levels of inheritance. I get a compiler error when I try to invoke the indirect base class's constructor.

class B
{
public:
    B() {} //trivial constructor
    virtual B* clone()=0;
};

template<class Base, class Derived>
class Clonable
    :public Base //weird, I know
{
public:
    virtual B* clone() {return new Derived(*this);}
};

class D1 : public Clonable<B, D1>
{
public:
    D1(int a); //non-trivial constructor. Different signature than B
};

class D2 : public Clonable<D1, D2>
{
public:
    D2(int a): D1(a) {} //compiler error here
}

The only solution I've come across so far is to use a variadic template constructor in Cloneable, but my compiler (VC++11) hasn't implemented them yet.

Lucretiel
  • 3,145
  • 1
  • 24
  • 52
  • AFAIK your compiler doesn't have that either but an inherited constructor could be preferred to a variadic, perfect-forwarding constructor in this case. This would look like `using Base::Base;` in the `Clonable` definition. Just as a heads-up! – Luc Danton Mar 01 '12 at 17:45

1 Answers1

7

You need to let your cloning "middleman" class forward constructor arguments, or better (Luc Danton suggested this) use C++11 constructor inheritance.

So, it's easy to do this in C++11, but it's not so easy in C++03 or with a current compiler that doesn't yet support C++11 argument forwarding or constructor inheritance, such as Visual C++10.

One way to do that in C++03, using a helper argument forwarder class, is discussed in my old blog posting "3 ways to mix in a generic cloning implementation". Then the middleman (cloning implementation) class can look like this:

template< class Derived, class Base >
class WithCloningOf
    : public progrock::cppx::ConstructorArgForwarder< Base >
{
protected:
    virtual WithCloningOf* virtualClone() const
    {
        return new Derived( *static_cast< Derived const* >( this ) );
    }

public:
    template< class ArgPack >
    WithCloningOf( ArgPack const& args )
        : progrock::cppx::ConstructorArgForwarder< Base >( args )
    {}

    std::auto_ptr< Derived > clone() const
    {
        return std::auto_ptr< Derived >(
            static_cast< Derived* >( virtualClone() )
            );
    }
};

I discussed the C++03 compatible ConstructorArgForwarder in earlier blog posting; it can look like this:

template< typename Type >
class ConstructorArgForwarder
    : public Type
{
public:
    typedef Type        Base;

    // TODO: remove
    virtual ~ConstructorArgForwarder() {}

    ConstructorArgForwarder( EmptyArgPack const& )
        : Base()
    {}

    template< class T01 >
    ConstructorArgForwarder(
        ArgPack< T01 > const& args
        )
        : Base( args.a01 )
    {}

    template< class T01, class T02 >
    ConstructorArgForwarder(
        ArgPack< T01, T02 > const& args
        )
        : Base( args.a01, args.a02 )
    {}

    template< class T01, class T02, class T03 >
    ConstructorArgForwarder(
        ArgPack< T01, T02, T03 > const& args
        )
        : Base( args.a01, args.a02, args.a03 )
    {}

    // And more, up to max 12 arguments.
};

It in turn uses an argument pack class ArgPack (well OK, class template), which can look like this:

enum NoArg {};

template<
    class T01 = NoArg, class T02 = NoArg, class T03 = NoArg,
    class T04 = NoArg, class T05 = NoArg, class T06 = NoArg,
    class T07 = NoArg, class T08 = NoArg, class T09 = NoArg,
    class T10 = NoArg, class T11 = NoArg, class T12 = NoArg
    >
struct ArgPack;

template<
    >
struct ArgPack<
    NoArg, NoArg, NoArg, NoArg, NoArg, NoArg,
    NoArg, NoArg, NoArg, NoArg, NoArg, NoArg
    >
{};

typedef ArgPack<
    NoArg, NoArg, NoArg, NoArg, NoArg, NoArg,
    NoArg, NoArg, NoArg, NoArg, NoArg, NoArg
    >                                           EmptyArgPack;

inline ArgPack<> args() { return ArgPack<>(); }

template<
    class T01
    >
struct ArgPack<
    T01, NoArg, NoArg, NoArg, NoArg, NoArg,
    NoArg, NoArg, NoArg, NoArg, NoArg, NoArg
    >
{
    T01 const&  a01;
    ArgPack( T01 const& v01 )
        : a01( v01 )
    {}
};

template< class T01 >
inline ArgPack< T01 >
args( T01 const& a01 )
{
    return ArgPack< T01 >( a01 );
}

Disclaimer: erors may just have sneaked in e.g. in copying the code from my blog. However, it worked at the time I posted about it, in May 2010.

Note: As I discuss at in the last of the two above blog postings, about cloning, there three main general ways to do it, and of these the simple macro beats the other two with good margin, for C++03. However, with C++11 the "middleman" approach you've chosen here seems better. The "sideways inheritance" via dominance is just complicated and inefficient, but if you are restricted to C++03, then do consider a simple macro!

Note 2: The last time I suggested doing the practical & sensible thing, I was heavily downvoted (presumably by Reddit kids). Since then, however, I have stopped caring about SO rep points, and in particular downvotes. So, happily, I can now again give good advice, just like in the old Usenet days, just ignoring them downvoter kids' mindless reaction to certain words. :-)

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • I'll probably end up using a macro, at least until I get a compiler with better C++11 support. I actually didn't even think of that, since I leapt at the opportunity to try out CRTP when my project started calling for it – Lucretiel Mar 01 '12 at 17:39
  • I had spent 1~2 days to try the CRTP cloneable solution, didn't find a clear and easy way to do. Even it works as this answer described, I don't know if it is worth to do it like that. Maybe will also end up with manually create the 'clone' function sadly. – user1633272 Mar 31 '17 at 13:25