Is it possible to clone a polymorphic object without manually adding overridden clone method into each derived class in C++?
当您想要复制多态类时,典型的模式是添加一个虚拟克隆方法并在每个派生类中实现它,如下所示:
1 2 3 4 | Base* Derived::clone() { return new Derived(*this); } |
然后在呼叫代码中,您可以:
1 2 | Base *x = new Derived(); Base *y = x->clone(); |
但是,如果您有50多个派生类,并且意识到您需要多态复制,那么将克隆方法复制粘贴到每个派生类中是很麻烦的。它本质上是一个围绕语言限制工作的样板文件,您必须拼写出调用构造函数的实际名称。
我没有跟上最近的特点,在最近的C++标准…在现代C++中有没有办法避免这种情况?
您可以使用这个通用的CRTP代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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> {}; |
它可以概括为智能指针和多个基础,如果需要的话。
您可以使用CRTP方法,但它还有其他缺点:
1 2 3 4 5 6 7 8 9 10 11 12 | 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)); } }; |
用途:
1 2 | class DerivedA : public BaseT<DerivedA> { }; |
1 2 | Base *x = new DerivedA(); Base *y = x->clone(); |
I haven't keep track with the new features in recent C++ standards... Is there a way to avoid this in modern C++?
这个技巧是从C++ 98标准中获得的。
如果可以控制如何传递多态类型,请使用类型擦除。特别是,当复制时,建议的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 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 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 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的Github存储库。
样品使用情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; } |
活在上帝的手中
您可能有一个类,在其中存储多态对象,并在其中克隆?与多态对象一起,您可以存储执行克隆的函数指针:
1 2 3 4 5 6 7 8 9 10 | 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); } |
从类本身的类型获取类名时,至少可以使用以下方法避免写入类名:
1 2 3 4 | struct A: public Base { Base* Clone() { return new std::remove_reference_t<decltype(*this)>(*this); } }; |
使用crtp并不能避免再次复制类名,因为您需要在crtp基的模板参数中写入类名。
可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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> {}; |