我正在使用带有CRTP的模板类来实现克隆模式,使用第二个模板参数Base来允许多级继承。当我尝试调用间接基类的构造函数时,我收到编译器错误。
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
}
到目前为止,我遇到的唯一解决方案是在Cloneable中使用可变参数模板构造函数,但我的编译器(VC ++ 11)尚未实现它们。
答案 0 :(得分:6)
你需要让你的克隆“中间人”类转发构造函数参数,或者更好(Luc Danton建议这样做)使用C ++ 11构造函数继承。
所以,在C ++ 11中很容易做到这一点,但是在C ++ 03中或者当前的编译器还不是那么容易支持C ++ 11参数转发或构造函数继承,例如Visual C ++ 10
在我的旧帖子"3 ways to mix in a generic cloning implementation"中讨论了使用辅助参数转发器类在C ++ 03中执行此操作的一种方法。然后中间人(克隆实现)类看起来像这样:
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() )
);
}
};
我在之前的博文中讨论了与C ++ 03兼容的ConstructorArgForwarder
;它看起来像这样:
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.
};
它反过来使用一个参数包类ArgPack
(好的,类模板),它看起来像这样:
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 );
}
免责声明:错误可能只是潜入,例如:从我的博客复制代码。但是,它在2010年5月我发布的时候起作用了。
注意:正如我在上面两篇博客文章的最后一篇中讨论的,关于克隆,有三种主要的一般方法,其中包括简单的宏
注2:我最后一次建议做实用的&amp;明智的事情,我被大量投票(大概是由Reddit孩子们)。然而,从那时起,我已经停止关注SO代表点,特别是暗示。所以,幸运的是,我现在可以再次给出好的建议,就像在旧的Usenet时代一样,只是忽略了他们对某些词语的无意识的反应。 : - )