Does C++11's decltype make clone unnecessary?

有些话、适合烂在心里 提交于 2019-11-29 09:31:08

decltype is a static construct. Like all C++ typing constructs, it cannot deduce the runtime type of an object. decltype(*original) is just Base&.

decltype (as its name suggests) gives the declared type (static type) of the expression that it is applied to.

decltype(*original) is Base&, so your code will print

Derived 1
Base
Derived 1

but no copy will be made in the third case.

Clone (or some variant of the pattern) is still the way to go in C++11.

n.m.

decltype cannot and does not recover dynamic object type. It is a purely static construct.

There's no magic way to copy an object. You must call a constructor of its exact final dynamic type somewhere.

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.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!