11

克隆范例用于制作派生类的副本,而无需向下转换为基类类型不幸的是,clone必须在每个子类中实现(或与 CRTP 一起使用 mixin)。

C ++ 11是否有可能decltype使这变得不必要?

我不认为下面的代码实际上是复制的original,而只是指向它的引用。当我尝试使用new decltype(*original)时,我得到一个错误: error: new cannot be applied to a reference type

仍然是cloneC++11 的路吗?或者是否有一些新方法可以使用 RTTI 从基类指针复制派生类对象?

#include <iostream>

struct Base
{
  virtual void print()
  {
    std::cout << "Base" << std::endl;
  }
};

struct Derived : public Base
{
  int val;
  Derived() {val=0;}
  Derived(int val_param): val(val_param) {}
  virtual void print()
  {
    std::cout << "Derived " << val << std::endl;
  }
};

int main() {
  Base * original = new Derived(1);
  original->print();

  // copies by casting down to Base: you need to know the type of *original
  Base * unworking_copy = new Base(*original);
  unworking_copy->print();

  decltype(*original) on_stack = *original;
  on_stack.print();
  return 0;
}
4

4 回答 4

21

decltype是一个静态结构。像所有 C++ 类型构造一样,它不能推断对象的运行时类型。decltype(*original)只是Base&

于 2012-05-03T02:43:04.950 回答
3

decltype(顾名思义)给出了它所应用的表达式的声明类型(静态类型)。

decltype(*original)Base&,所以你的代码将打印

Derived 1
Base
Derived 1

但在第三种情况下不会制作副本。

克隆(或该模式的某些变体)仍然是 C++11 的方式。

于 2012-05-03T02:44:57.827 回答
3

decltype不能也不会恢复动态对象类型。它是一个纯粹的静态构造。

没有复制对象的神奇方法。您必须在某处调用其确切的最终动态类型的构造函数。

于 2012-05-03T02:55:33.827 回答
2

Is clone still the way to go in C++11? Or is there some new way to use RTTI to copy a derived class object from a base class pointer?

In case anybody is interested in a non-invasive cloning, C++11's lambdas seem to provide some new cloning facilities: Thinking about the issue of cloning, I came to admit that the one who manufactured the original instance of an object should also be the one who can help build a replica. Consider the case where all of your objects are manufactured by some Factory. In this case, additionally to the usual create interface, your factory could be equipped with a clone interface, e.g. like so:

#include <iostream>
#include <functional>
#include <memory>
#include <unordered_map>

template <typename BASE>
struct
Factory {
    private: using
    TCloneFn = std::function<std::shared_ptr<BASE>(BASE const * const)>;

    private:
    static std::unordered_map<BASE const*,TCloneFn> cloneFnMap;

    public: template <typename DERIVED_TYPE, typename...TS>
    static std::shared_ptr<BASE>
    create(TS...args) {
        BASE* obj = new DERIVED_TYPE(args...);
        const std::shared_ptr<BASE> pNewObj =
            std::shared_ptr<BASE>(
                obj,
                [&](BASE* p){
                    cloneFnMap.erase(p);
                    delete p;
                }
            );

        cloneFnMap[obj] = [&](BASE const * const orig){
            std::shared_ptr<BASE> pClone = create<DERIVED_TYPE>(std::ref(static_cast<DERIVED_TYPE const &>(*orig)));
            return pClone;
        };
        return pNewObj;
    }

    public: static std::shared_ptr<BASE>
    clone(std::shared_ptr<BASE const> original) {
        return cloneFnMap[original.get()](original.get());
    }
};

template <typename BASE> std::unordered_map<BASE const*,typename Factory<BASE>::TCloneFn> Factory<BASE>::cloneFnMap;

class Base {
    public: virtual ~Base() throw() {}
    public: virtual void whoAmI() const {
        std::cout << "I am Base instance " << this << "\n";
    }
};


class Derived : public Base {
    std::string name;
    public: Derived(std::string name) : name(name) {}
    public: Derived(const Derived&other) : name("copy of "+other.name) {
    }
    private: virtual void whoAmI() const {
        std::cout << "I am Derived instance " << this << " " << name << "\n";
    }
};

int main() {
    std::shared_ptr<Base> a = Factory<Base>::create<Derived>("Original");
    a->whoAmI();
    std::shared_ptr<Base> copy_of_a = Factory<Base>::clone(a);
    copy_of_a->whoAmI();
    std::shared_ptr<Base> copy_of_a_copy = Factory<Base>::clone(copy_of_a);
    copy_of_a_copy->whoAmI();
    return 0;
}

The trick is to remember how the original was constructed in the Factory::create method (by associating a pointer to the object with a lambda that will invoke the copy-constructor). Never throw away old blueprints, you may need them later-on;-)

Frankly, I still prefer the old clone solution with CRTP etc, but this may trigger some new ideas or come otherwise in handy.

The code above is also at http://ideone.com/kIPFt2

EDIT: As wjl commented, the first version of the code would cause cloneFnMapto steadily grow. After thinking it over, I decided to fix the code by introducing a custom deleter to the shared_ptrs being returned such that the cloneFnMap will also get cleaned up. Still, this should just be considered an experimental proof-of-concept code.

于 2015-02-21T15:01:58.553 回答