[C++] Является ли std :: unique_ptr <T> необходимым для определения полного определения T?


Answers

Компилятор нуждается в определении Thing для создания деструктора по умолчанию для MyClass. Если вы явно объявляете деструктор и перемещаете его (пустую) реализацию в файл CPP, код должен компилироваться.

Question

У меня есть код в заголовке, который выглядит так:

#include <memory>

class Thing;

class MyClass
{
    std::unique_ptr< Thing > my_thing;
};

Если я включу этот заголовок в cpp, который не включает определение типа Thing , то это не компилируется под VS2010-SP1:

1> C: \ Program Files (x86) \ Microsoft Visual Studio 10.0 \ VC \ include \ memory (2067): ошибка C2027: использование неопределенного типа 'Thing'

Замените std::unique_ptr на std::shared_ptr и он скомпилируется.

Итак, я предполагаю, что это текущая реализация VS2010 std::unique_ptr unique_ptr, которая требует полного определения и полностью зависит от реализации.

Или это? Есть ли что-то в его стандартных требованиях, что делает невозможным реализацию проекта std::unique_ptr только для прямого объявления? Это странно, поскольку он должен держать указатель на Thing , не так ли?




Просто для полноты:

Заголовок: Ах

class B; // forward declaration

class A
{
    std::unique_ptr<B> ptr_;  // ok!  
public:
    A();
    ~A();
    // ...
};

Источник A.cpp:

class B {  ...  }; // class definition

A::A() { ... }
A::~A() { ... }

Определение класса B должно рассматриваться конструктором, деструктором и всем, что могло бы неявно удалить B. (Хотя конструктор не отображается в списке выше, в VS2017 даже конструктору необходимо определение B. И это имеет смысл при рассмотрении что в случае исключения в конструкторе unique_ptr снова уничтожается.)




Похоже, что текущие ответы не совсем сбивают с толку, почему конструктор по умолчанию (или деструктор) является проблемой, но пустые, объявленные в cpp, не являются.

Вот что происходит:

Если внешний класс (т.е. MyClass) не имеет конструктора или деструктора, тогда компилятор генерирует значения по умолчанию. Проблема заключается в том, что компилятор по существу вставляет пустой конструктор / деструктор по умолчанию в файл .hpp. Это означает, что код для contructor / destructor по умолчанию компилируется вместе с двоичным исполняемым файлом исполняемого файла, а не двоичными файлами вашей библиотеки. Однако эти определения не могут действительно построить частичные классы. Поэтому, когда компоновщик входит в двоичный файл библиотеки и пытается получить конструктор / деструктор, он не находит и вы получаете ошибку. Если код конструктора / деструктора был в вашем .cpp, то в бинарнике библиотеки есть доступное для ссылки.

Таким образом, это не связано с использованием unique_ptr вместо shared_ptr для сценария выше, если вы используете современные компиляторы (старый компилятор VC ++ может иметь ошибку в реализации unique_ptr, но VC ++ 2015 отлично работает на моей машине).

Итак, мораль этой истории состоит в том, что ваш заголовок должен оставаться свободным от определения конструктора / деструктора. Он может содержать только их декларацию. Например, ~MyClass()=default; в hpp не будет работать. Если вы разрешите компилятору вставлять конструктор по умолчанию или деструктор, вы получите ошибку компоновщика.

Еще одна сторона примечания: если вы все еще получаете эту ошибку даже после того, как у вас есть конструктор и деструктор в файле cpp, скорее всего, причина в том, что ваша библиотека не скомпилирована должным образом. Например, однажды я просто изменил тип проекта из Консоли в библиотеку в VC ++, и я получил эту ошибку, потому что VC ++ не добавил символ препроцессора _LIB и создал то же самое сообщение об ошибке.