我想为各种类层次结构提供适当的克隆机制。似乎是一个合理的想法,我拼凑了一个基本的解决方案,使用CRTP在派生类中实现必要的clone()
函数。
我进一步用模板模板参数模板化它以允许策略控制克隆的存储/所有权:
template <typename base, typename derived>
struct clone_raw
{
typedef derived * return_type;
static return_type clone(const base * original) { return new derived(static_cast<const derived &>(*original)); }
};
template <typename base, typename derived>
struct clone_shared
{
typedef std::shared_ptr<derived> return_type;
static return_type clone(const base * original) { return std::make_shared<derived>(static_cast<const derived &>(*original)); }
};
template <typename base, typename derived>
struct clone_unique
{
typedef std::unique_ptr<derived> return_type;
static return_type clone(const base * original) { return std::make_unique<derived>(static_cast<const derived &>(*original)); }
};
// derived class CRTP without base CRTP helper
template <typename base, typename derived, template <typename,typename> typename policy = clone_raw>
class clonable : public base
{
public:
// define our derived's parent class
using parent = clonable<base, derived, policy>;
// constructor forwarding (enable all constructors)
using base::base;
// clone using policy
auto clone() const
{
return policy<base, derived>::clone(this);
}
};
这种方法运行得相当好,每个派生类都需要使用CRTP来调用上述机制。
class Example
{
public:
virtual std::shared_ptr<Example> clone() const = 0;
virtual void explain() const = 0;
};
class Ex1 : public clonable<Example, Ex1>
{
public:
Ex1(const char * text) : m_text(text) {}
void explain() const override { std::cout << m_text; }
private:
const char * m_text;
};
class Ex2 : public clonable<Ex1, Ex2>
{
public:
Ex2(const char * text, const char * extra) : parent(text), m_extra(extra) {}
void explain() const override { parent::explain(); std::cout << " " << m_extra; }
private:
const char * m_extra;
};
但是,这使得基类需要实现root虚拟clone()方法,这意味着层次结构中的所有位置都必须反复声明克隆策略。对于良好做法/常识/效率/正确性,这当然是默认/等等。
所以,我想,我如何制作两个协同工作的CRTP模板,一个为基类提供带有正确签名的初始虚拟clone()
,然后是一个使用其父类的派生CRTP确定要使用的正确克隆策略,以便只需在根类中指定一次策略,并且所有派生类都将实现正确的clone()
覆盖,智能地确定基类正在使用哪个策略
但是,我无法弄清楚,是如何将策略模板模板暴露给派生的CRTP模板,以便他们不需要采取任何明确的策略参数 - 来实现设计理念
// base class CRTP
template <typename base, template <typename, typename> typename policy = clone_raw>
class clonable_base : base
{
public:
// define our derived's parent class
using parent = clonable_base<base, policy>;
// constructor forwarding (enable all constructors)
using base::base;
using clone_policy = policy<base, base>;
using clone_type = typename clone_policy::return_type;
// clone using policy
virtual clone_type clone() const = 0
{
return clone_policy::clone(this);
}
};
所以这里的百万美元问题是:如何将policy
模板模板公开给以下派生的CRTP :
// derived class CRTP with base CRTP helper
template <typename base, typename derived>
class clonable_derived : public base
{
public:
// define our derived's parent class
using parent = clonable_derived<base, derived>;
// constructor forwarding (enable all constructors)
using base::base;
using policy = base::policy; // ???
using clone_policy = policy<base,derived>;
using clone_type = typename clone_policy::return_type;
// clone using policy
clone_type clone() const override
{
return clone_policy::clone(this);
}
};
似乎所有内容都已到位,但我对如何公开策略模板模板感到困惑,以便派生的可克隆类型可以访问它以实例化其基本/派生对的相应策略?!!
答案 0 :(得分:5)
使用别名模板:
template <class A, class B>
using policy = typename base::template policy<A,B>;
答案 1 :(得分:1)
好的 - Barry的回答是正确的 - 模板别名绝对是正确的工具。
我的原始代码中存在各种问题,包括一个问题,即除非是协变的,否则无法更改虚拟的返回类型,unique_ptr
和shared_ptr
似乎不支持协方差(至少不在VC ++ 2015 Update 3中)。
所以,这是工作代码 - 我当然愿意接受改进建议!
// cloning policies:
// clone_raw = new
// clone_shared = std::shared<>
// clone_unique = std::unique<>
template <class base, class derived>
struct clone_raw
{
using return_type = base *;
static return_type clone(const base * original) { return new derived(static_cast<const derived &>(*original)); }
};
template <class base, class derived>
struct clone_shared
{
typedef std::shared_ptr<base> return_type;
static return_type clone(const base * original) { return std::make_shared<derived>(static_cast<const derived &>(*original)); }
};
template <class base, class derived>
struct clone_unique
{
typedef std::unique_ptr<base> return_type;
static return_type clone(const base * original) { return std::make_unique<derived>(static_cast<const derived &>(*original)); }
};
// base class CRTP
template <class base, template <class, class> typename policy = clone_raw>
class clonable_base
{
public:
// define our derived's parent class
using parent = clonable_base<base, policy>;
template <class derived>
using policy_alias = policy<base, derived>;
using clone_policy = policy_alias<base>;
using clone_type = typename clone_policy::return_type;
// clone using policy
virtual clone_type clone() const = 0;
};
// derived class CRTP with base CRTP helper
template <typename base, typename derived>
class clonable_derived : public base
{
public:
// define our derived's parent class
using parent = clonable_derived<base, derived>;
// constructor forwarding (enable all constructors)
using base::base;
template <class derived>
using policy_alias = typename base::template policy_alias<derived>;
using clone_policy = typename policy_alias<derived>;
using clone_type = typename clone_policy::return_type;
// clone using policy
clone_type clone() const override
{
return clone_policy::clone(this);
}
};
这是一个微不足道的测试:
class Example : public clonable_base<Example, clone_shared>
{
public:
virtual void explain() const = 0;
};
class Ex1 : public clonable_derived<Example, Ex1>
{
public:
Ex1(const char * text) : m_text(text) {}
void explain() const override { std::cout << m_text; }
private:
const char * m_text;
};
class Ex2 : public clonable_derived<Ex1, Ex2>
{
public:
Ex2(const char * text, const char * extra) : parent(text), m_extra(extra) {}
void explain() const override { parent::explain(); std::cout << " " << m_extra; }
private:
const char * m_extra;
};
int main()
{
Ex1 ex1("example 1");
Ex2 ex2("example 2", "this is derived derived example");
auto clone = ex2.clone();
ex1.explain();
std::cout << std::endl;
ex2.explain();
std::cout << std::endl;
clone->explain();
std::cout << std::endl;
return 0;
}