C++11 контейнер unique_ptr для заранее объявленного класса

g++ -std=c++11 не компилирует класс, который содержит контейнер, содержащий уникальные указатели, которые указывают на объявленный вперед класс. Вопросы:

  • Зачем?
  • Есть ли разумное решение?

Пример кода:

#include <vector>
#include <memory>

// variant 1 (with full class definition): compiles
class Bar { [..] }; 
using BarPtr = std::unique_ptr<Bar>;

// variant 2 (with shared instead of unique pointers): compiles
using BarPtr = std::shared_ptr<class Bar>;

// variant 0 (which is what we want): compilation fails below
using BarPtr = std::unique_ptr<class Bar>;

// end of variants

class Foo {
    std::vector<BarPtr> vec;
 public:
    Foo() {} // compilation of variant 0 fails here:
             // In instantiation of ‘void std::default_delete<Bar>::operator()(Bar*) const
             // ... 
             // invalid application of ‘sizeof’ to incomplete type ‘Bar’
};

Я видел, как переслать объявление класса для использования в стандартном контейнере unique_ptr и требуется ли std::unique_ptr, чтобы знать полное определение T?, но не нахожу убедительных ответов на мои вышеуказанные вопросы.

4 ответа

Вам нужно переместить те части Foo в файл реализации, для которых необходимо полное определение Bar (см. Таблицу Говарда Хиннанта: /questions/22207241/trebuetsya-li-stduniqueptrt-znat-polnoe-opredelenie-t/22207259#22207259). Следуя этому руководству, компилируется:

#include <vector>
#include <memory>

class Bar;
using BarPtr = std::unique_ptr<Bar>;

class Foo {
    std::vector<BarPtr> vec;
 public:
    Foo(); // Needs Bar in implementation
    ~Foo();// Needs Bar in implementation
};

Сообщение об ошибке в строке

Foo() {};

кажется, говорит, что деструктор ~Bar() необходимо. Но почему? Эта часть моего вопроса все еще открыта.

Что касается практического решения, однако, ответ прост: замените вышеуказанную строку на

Foo();

и реализовать Foo::Foo в модуле компиляции, который видит полное определение класса Bar,

Вы можете сделать это:

#include <vector>
#include <memory>

class Bar;
using BarPtr = std::unique_ptr<Bar>;

class Foo {
    std::vector<BarPtr> vec;
 public:
    Foo() {} // variant 3 fails here:
             // invalid application of ‘sizeof’ to incomplete type ‘Bar’
};


//Implementation goes here
class Bar{};
int main(){
    Foo a;
}

Live Demo

Проблема в том, что вы не можете удалить указатель на неполный (объявленный в будущем) тип. Убедитесь, что определение Bar отображается в деструкторе содержащего класса, или используйте пользовательское средство удаления для unique_ptr это видит. Смотрите также std::unique_ptr с неполным типом не будет компилироваться

Другие вопросы по тегам