Неполный тип для std::vector

Компилятор GCC жалуется (см. ниже), когда я пытаюсь сделать следующее. class Face должен быть неполным, потому что он содержит указатель на class Element, который также содержит указатель на class Face. Другими словами, существует круговая зависимость между классами. Как я могу это исправить?

ошибка: неверное применение sizeof к неполному типу Face

class Face; // needs to be incomplete

class Element
{
    std::vector < std::unique_ptr <Face> > face;
};

class Face
{
    std::vector < std::unique_ptr <Element> > elm;
};

person Shibli    schedule 01.06.2014    source источник


Ответы (1)


Один из способов исправить это — объявить деструкторы и конструкторы Element и Face, но не определять их в заголовке. Затем вам нужно определить их в файлах cpp.

(Более технические подробности можно найти в ответах на мой вопрос: of-t">Обязательно ли std::unique_ptr‹T› знать полное определение T?)

Источником проблемы является то, что деструктор unique_ptr должен вызывать delete (по умолчанию), поэтому ему необходимо знать определение типа (чтобы иметь его размер). Но если деструктор Element и Face генерируется автоматически, то он будет встроен по умолчанию: код, использующий экземпляры Element и Face, будет вынужден знать размер обоих типов, чтобы их деструкторы могли вызывать деструктор unique_ptr, который может вызывать delete с помощью тип, связанный с указателем.

Решение, которое я дал, обеспечит создание и уничтожение unique_ptr в отдельном cpp. Они не будут встроены, но их по-прежнему можно вызывать из кода, использующего Element и Face. Код деструктора unique_ptrs будет находиться в cpp, где определены деструкторы Element и Face, поэтому в этих cpp потребуется определение обоих.

Чтобы взять ваш пример:

//header
class Face; // needs to be incomplete

class Element
{
public:
    Element(); // don't define it here
    ~Element(); // don't define it here
private:
    std::vector < std::unique_ptr <Face> > face;
};

class Face
{
public:
    Face(); // don't define it here
    ~Face(); // don't define it here
private:
    std::vector < std::unique_ptr <Element> > elm;
};

// cpp 
#include "header"
// if you want the default impl (C++11)
Element::Element() = default; 
Element::~Element() = default; 

Face::Face() = default; 
Face::~Face() = default; 

Если они находятся в разных парах header/cpp, это все равно одно и то же решение. Однако вам нужно сделать больше форвардного объявления, а файлы cpp, определяющие построение/уничтожение, должны включать все необходимые заголовки:

//element.h
class Face; // needs to be incomplete

class Element
{
public:
    Element(); // don't define it here
    ~Element(); // don't define it here
private:
    std::vector < std::unique_ptr <Face> > face;
};

////////////////////////////////////////////////////////////
// face.h
class Element; // needs to be incomplete

class Face
{
public:
    Face(); // don't define it here
    ~Face(); // don't define it here
private:
    std::vector < std::unique_ptr <Element> > elm;
};

////////////////////////////////////////////////////////////
// element.cpp 
#include "element.h"
#include "face.h" // necessary to allow the unique_ptr destructor to call delete

// if you want the default impl (C++11)
Element::Element() = default; 
Element::~Element() = default; 

////////////////////////////////////////////////////////////
// face.cpp 
#include "element.h" // necessary to allow the unique_ptr destructor to call delete
#include "face.h" 

// if you want the default impl (C++11)
Face::Face() = default; 
Face::~Face() = default; 
person Klaim    schedule 01.06.2014
comment
Что, если Face и Element имеют свои собственные файлы заголовков и cpp? - person Shibli; 02.06.2014
comment
@Shibli Тот же ответ, но тогда вам просто нужно объявить Element для Face и Face для Element. Я добавлю пример. - person Klaim; 02.06.2014
comment
Но если деструктор Element и Face генерируется автоматически, то он будет встроен по умолчанию: t Да, действительно. Но почему проблема в том, что они встроены? - person Gilgamesz; 21.04.2016