Forwarding Constructor with CRTP(使用CRTP的转发构造器)
问题描述
我使用带有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)还没有实现它们。
推荐答案
您需要让您的克隆"Midleman"类转发构造函数参数,或者更好的(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月发表关于它的帖子时,它起作用了。
注意:正如我在上面两篇关于克隆的博客文章的最后一篇中所讨论的那样,有三种主要的一般方法来完成这项工作,其中简单的宏在C++03中以很好的优势击败了其他两种方法。然而,对于C++11,您在这里选择的"中间人"方法似乎更好。通过支配的"横向继承"非常复杂和低效,但是如果您被限制在C++03上,那么一定要考虑一个简单的宏!
注2:上一次我建议做实际和明智的事情时,我遭到了强烈的反对(想必是Reddit的孩子们)。然而,从那时起,我就不再关心这样的代表点,特别是反对票。因此,令人高兴的是,我现在可以再一次给出好的建议,就像在过去的Usenet时代一样,只是忽略了那些下选民的孩子对某些单词的愚蠢反应。:-)这篇关于使用CRTP的转发构造器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:使用CRTP的转发构造器
基础教程推荐
- C++,'if' 表达式中的变量声明 2021-01-01
- 调用std::Package_TASK::Get_Future()时可能出现争用情况 2022-12-17
- C++ 程序在执行 std::string 分配时总是崩溃 2022-01-01
- 您如何将 CreateThread 用于属于类成员的函数? 2021-01-01
- C++ 标准:取消引用 NULL 指针以获取引用? 2021-01-01
- 如何在 C++ 中处理或避免堆栈溢出 2022-01-01
- 如何定义双括号/双迭代器运算符,类似于向量的向量? 2022-01-01
- 什么是T&&(双与号)在 C++11 中是什么意思? 2022-11-04
- 运算符重载的基本规则和习语是什么? 2022-10-31
- 设计字符串本地化的最佳方法 2022-01-01