С++ 11 decltype делает клон ненужным? - программирование
Подтвердить что ты не робот

С++ 11 decltype делает клон ненужным?

Парадокс clone используется для создания копии производного класса без кавычек до типа базового класса. К сожалению, clone должен быть реализован в каждом подклассе (или с mixin с CRTP).

Есть ли вероятность, что С++ 11 decltype делает это ненужным?

Я не думаю, что приведенный ниже код действительно копирует original, но просто указывает на него ссылку. Когда я пытался использовать new decltype(*original), я получаю сообщение об ошибке: error: new cannot be applied to a reference type.

Есть ли еще clone путь в С++ 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;
}
4b9b3361

Ответ 1

decltype - статическая конструкция. Как и все конструкторы ввода С++, он не может вывести тип среды выполнения объекта. decltype(*original) - это просто Base&.

Ответ 2

decltype (как следует из названия) дает объявленный тип (статический тип) выражения, к которому он применяется.

decltype(*original) Base&, поэтому ваш код будет печатать

Derived 1
Base
Derived 1

но в третьем случае копия не будет выполнена.

Клон (или некоторый вариант шаблона) по-прежнему остается в С++ 11.

Ответ 3

decltype не может и не восстанавливает динамический тип объекта. Это чисто статическая конструкция.

Нет никакого волшебного способа скопировать объект. Вы должны вызвать конструктор своего точного окончательного динамического типа где-нибудь.

Ответ 4

Достаточно ли клонировать путь к С++ 11? Или существует какой-то новый способ использования RTTI для копирования объекта производного класса из указателя базового класса?

В случае, если кто-то заинтересован в неинвазивном клонировании, С++ 11 lambdas, похоже, предоставляют некоторые новые возможности клонирования: размышляя о проблеме клонирования, я пришел к выводу, что тот, кто изготовил оригинальный экземпляр объекта также должен быть тот, кто может помочь построить реплику. Рассмотрим случай, когда все ваши объекты производятся некоторым Factory. В этом случае, помимо обычного интерфейса create, ваш factory может быть оснащен интерфейсом clone, например. так:

#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;
}

Трюк заключается в том, чтобы запомнить, как оригинал был создан в методе Factory::create (путем сопоставления указателя на объект с лямбдой, который будет вызывать экземпляр-конструктор). Никогда не выбрасывайте старые чертежи, вам может понадобиться их позже; -)

Честно говоря, я по-прежнему предпочитаю старое решение clone с CRTP и т.д., но это может вызвать некоторые новые идеи или пригодиться в другом случае.

Вышеприведенный код также находится в http://ideone.com/kIPFt2

EDIT: Как прокомментировал wjl, первая версия кода приведет к постоянному росту cloneFnMap. Подумав об этом, я решил исправить код, введя пользовательский отладчик в возвращаемый shared_ptr так, чтобы cloneFnMap также был очищен. Тем не менее, это следует просто рассматривать как экспериментальный код концептуальной концепции.