要复制多态类的典型模式是添加一个虚拟克隆方法,并在每个派生类中实现它,如下所示:
Base* Derived::clone()
{
return new Derived(*this);
}
然后在调用代码中可以:
Base *x = new Derived();
Base *y = x->clone();
但是,如果您有50多个派生类并且意识到需要多态复制,那么将克隆方法复制粘贴到每个复制类中就很麻烦。实际上,这是一个样板,可以解决语言限制,您必须拼出实际名称才能调用构造函数。
我没有跟踪最新的C ++标准中的新功能...在现代C ++中有什么方法可以避免这种情况?
答案 0 :(得分:24)
您可以使用此通用CRTP代码
template <class Derived, class Base>
struct Clonable : Base {
virtual Base* do_clone() {
return new Derived(*static_cast<Derived*>(this));
}
Derived* clone() { // not virtual
return static_cast<Derived*>(do_clone());
}
using Base::Base;
};
struct empty {};
struct A : Clonable<A, empty> {};
struct B : Clonable<B, A> {};
如果需要,可以将其推广到智能指针和多个基数。
答案 1 :(得分:10)
您可以使用CRTP方法,但这还有其他缺点:
struct Base {
virtual Base* clone() const = 0;
};
template <typename Derived>
class BaseT : public Base {
// ...
public:
Base* clone() const override {
return new Derived(*static_cast<Derived*>(this));
}
};
用法:
class DerivedA : public BaseT<DerivedA> {
};
Base *x = new DerivedA();
Base *y = x->clone();
我没有跟踪最新的C ++标准中的新功能...在现代C ++中有什么方法可以避免这种情况?
从c ++ 98标准开始可以使用此技巧。
答案 2 :(得分:2)
如果可以控制如何传递多态类型,请使用类型擦除。特别是,proposed std::polymorphic_value
在复制派生副本构造函数时会对其进行调用。您可以将其想象成这样:
template <typename B>
class polymorphic_value {
public:
template <typename D,
std::enable_if_t<
std::is_base_of<B, std::decay_t<D>>::value, int> = 0>
explicit polymorphic_value(D&& value)
: ptr{std::make_unique<derived_t<std::decay_t<D>>>(std::forward<D>(value))}
{}
polymorphic_value(polymorphic_value const& rhs)
: ptr{rhs.ptr->clone()}
{}
B const& get() const { return ptr->get(); }
B& get() {
// Safe usage of const_cast, since the actual object is not const:
return const_cast<B&>(ptr->get());
}
private:
struct base_t {
virtual ~base_t() = default;
virtual std::unique_ptr<B> clone() const = 0;
// With more effort, this doesn't have to be a virtual function.
// For example, rolling our own vtables would make that possible.
virtual B const& get() const = 0;
};
template <typename D>
struct derived_t final : public base_t {
explicit derived_t(D const& d)
: value{d}
{}
explicit derived_t(D&& d)
: value{std::move(d)}
{}
std::unique_ptr<B> clone() const override {
return std::make_unique<D>(value);
}
B const& get() const override {
return value;
}
D value;
};
std::unique_ptr<base_t> ptr;
};
有关提案之后的全面实施,请参见jbcoe's github repository。
样品用量:
class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
public:
Derived() = default;
Derived(Derived const&);
};
int main() {
polymorphic_value<Base> it{Derived{}};
auto const copy = it;
}
答案 3 :(得分:1)
您至少可以通过以下方式从类本身的类型中获取类名,从而避免编写类名:
struct A: public Base
{
Base* Clone() { return new std::remove_reference_t<decltype(*this)>(*this); }
};
使用CRTP不能避免避免再次重复类名,因为您必须将类名写在CRTP基本模板模板中。
答案 4 :(得分:1)
您可能有一个用于存储多态对象并要克隆的类?与您的多态对象一起,您可以存储执行克隆的功能指针:
template<class Derived>
Base* clone(const Base* b) {
return new Derived(static_cast<const Derived*>(b));
}
void SampleUsage() {
Base* b = new Derived;
Base*(*cloner)(const Base*) = clone<Derived>;
Base* copy = cloner(b);
}
cloner
的类型与派生无关。就像简化的std :: function一样。
答案 5 :(得分:0)
您可以使用CRTP
在派生类和实现克隆方法的基础之间添加一个附加层。
struct Base {
virtual ~Base() = default;
virtual Base* clone() = 0;
};
template <typename T>
struct Base_with_clone : Base {
Base* clone() {
return new T(*this);
}
};
struct Derived : Base_with_clone<Derived> {};