Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a generic way to forward constructor arguments?

I have a working Cloneable/CloneableImpl class pair below. It does its job as long as I have default constructors from child to parent.

Suppose Animal's constructor is modified to Animal( std::string const& name ), which needs the name to be passed up from the children class constructors.

How can I incorporate this requirement into the structure while keeping Cloneable/CloneableImpl generic?

In other words, I need to be able forward all constructor arguments from Lion, Tiger up to Animal. Is there a way to do this in C++11 in a generic manner?

If it's not possible, how can these templates be restructured to remain generic while allowing for the constructor requirement?

Code

template<typename P>
struct Cloneable
{
    virtual P* clone() const = 0;
};

template<typename T,typename P>
struct CloneableImpl :
    public P
{
    virtual P* clone() const 
    {
        return new T( dynamic_cast<T const&>(*this));
    }
};

// ----------------------------------------------------------------------------

struct Animal :
    public Cloneable<Animal>
{ 
};

struct Lion  : 
    public CloneableImpl<Lion,Animal> 
{ 
};

struct Tiger : 
    public CloneableImpl<Tiger,Animal> 
{ 
};

int
main( int argv, char* argc[] )
{
    Animal* x = new Lion;
    Animal* y = x->clone();

    // we want to do this without hard-coding in template classes
    // Animal* z = new Lion( "Samba" );  
}
like image 452
kfmfe04 Avatar asked Jan 02 '13 12:01

kfmfe04


1 Answers

Following @Cheersandhth.-Alf and @R. Martinho Fernandes' suggestions in the Comments to the OP to look into perfect forwarding. I researched a bit and came up with this, which seems to work.

Thanks guys!

CODE

#include <string>
#include <iostream>

template<typename P>
struct Cloneable
{
    virtual P* clone() const = 0;
};

template<typename T,typename P>
struct CloneableImpl :
    public P
{
    template<typename... Args>
    CloneableImpl( Args&&... args ) 
       : P(std::forward<Args>(args)...)
  {  }

    virtual P* clone() const 
    {
        return new T( dynamic_cast<T const&>(*this));
    }
};

// ----------------------------------------------------------------------------

struct Animal :
    public Cloneable<Animal>
{ 
    Animal( std::string const& name ) : m_name( name ) { }

    std::string  m_name;
};

struct Lion  : 
    public CloneableImpl<Lion,Animal> 
{ 
    template<typename... Args>
    Lion( Args&&... args ) 
       : CloneableImpl<Lion,Animal>(std::forward<Args>(args)...)
  {  }
};

struct Tiger : 
    public CloneableImpl<Tiger,Animal> 
{ 
};

int
main( int argv, char* argc[] )
{
    Animal* x = new Lion( "Samba" );
    Animal* y = x->clone();

    std::cerr << y->m_name << std::endl;
} 
like image 109
kfmfe04 Avatar answered Nov 20 '22 20:11

kfmfe04