Умные указатели на производные типы в иерархиях классов polymorphi c, ошибка компиляции - PullRequest
1 голос
/ 26 апреля 2020

Рассмотрим следующую структуру интерфейса моего класса. Я представляю два случая, один с использованием shared_ptr<T>, а другой с использованием unique_ptr<T>. Инстанцируемые пользовательские типы не являются шаблонами. Только базовый конструктор основан на том, что ему нужно знать производный тип для хранения указателя на него. Я не показываю стандартную библиотеку для проверки, является ли аргумент шаблона производным типом родительского класса, чтобы сделать код проще и легче для чтения.

class Foo;

template<typename DerivedType>
class Base {
protected:
    std::shared_ptr<DerivedType> derived_ptr_{nullptr};
    Foo internal_object_{};
public:
    virtual ~Base() {};

    std::shard_ptr<DerivedType> derived_ptr() { return derived_ptr_; }
    Foo* { return &internal_object_; }

    virtual void initialize(/*.../*) = 0;
    virtual void create(/*...*/) = 0;
    virtual void copyTo(/*...*/) = 0;
    virtual void update(/*...*/) = 0;
    virtual void cleanup() = 0;

protected:
    Base() = default;
};

class Derived : public Base<class Derived> {
public:
    virtual ~Derived() = default;
    Derived() = default;

    virtual void initialize(/*.../*) override {/*...*/};
    virtual void create(/*...*/) override{
        /*...*/

        // After all creation is successful!
        derived_ptr_ = std::make_shared<Derived>(*this);
    };

    virtual void copyTo(/*...*/) override{/*...*/};
    virtual void update(/*...*/) override{/*...*/};
    virtual void cleanup() override{/*...*};
}

Выше скомпилированы. Я не проверял его на связывание или время выполнения, но он компилируется.


Теперь давайте рассмотрим второй случай, все точно так же, как указано выше, за исключением того, что все экземпляры std::shared_ptr<T> заменены на std::unqiue_ptr<T> переменная-член в базовом классе, метод получения и назначение или создание интеллектуального указателя с использованием ptr = std::make_unique<T>(*this). За исключением случая компиляции std::unique_ptr<T> в Visual Studio 2017 с флагом языка компилятора, установленным на /std:c++latest, который генерирует эту ошибку компилятора:

Примечание: - Имя из классов в моем проекте Buffer и VertexBuffer соответственно. Это одно из основных отличий от того, что вы видите в приведенном выше примере. Это и я вынул их из пространства имён для простоты. Я также удалил проверку параметра шаблона, чтобы убедиться, что его тип аргумента является производным типом для базового класса.

1>------ Build started: Project: Force Engine, Configuration: Debug x64 ------
1>Buffer.cpp
1>c:\users\..\buffer.h(62): error C2440: '<function-style-cast>': cannot convert from 'VertexBuffer' to 'std::unique_ptr<BufferClassType,std::default_delete<_Ty>>'
1>        with
1>        [
1>            BufferClassType=VertexBuffer,
1>            _Ty=VertexBuffer
1>        ]
1>c:\users\...\buffer.h(62): note: No constructor could take the source type, or constructor overload resolution was ambiguous
1>Done building project "Force Engine.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

Мне интересно, почему он компилируется в регистр std::shared_ptr<T>, но не компилируется в случае std::unique_ptr<T>. Что заставляет его терпеть неудачу, как решить эту проблему, и есть ли способ обойти это. Я знаю, что могу использовать std::shared_ptr и передать его вызывающей стороне, которая в этом нуждается, но я не особо хочу подсчет ссылок для этого указателя. Я хотел бы, чтобы родительский класс (владел) указателем производного класса, в то же время предоставляя интерфейс, позволяющий внешним вызовам иметь к нему доступ. В контексте наблюдателя или с возможностью создания собственного указателя, но никогда не являющегося владельцем объекта. Класс или классы, которые будут содержать экземпляры производного класса, будут владеть самим производным классом, но не его внутренним указателем. Относится ли эта ошибка компилятора к тому, что unique_ptr s нельзя скопировать или что-то подобное?

1 Ответ

1 голос
/ 26 апреля 2020

std::make_unique<Derived>(*this) пытается скопировать-создать экземпляр Derived. Но Derived не копируется, потому что его базовый класс Base<Derived> не копируется, потому что его элемент данных std::unique_ptr<DerivedType> derived_ptr_ не копируется, потому что std::unique_ptr не копируется.

С shared_ptr, все копируется и код работает.

...