std::vector的不完整类型

14

当我尝试以下操作时,GCC编译器会抱怨(见下文)。class Face需要是不完整的,因为它包含指向class Element的指针,而后者同样包含指向class Face的指针。换句话说,这些类之间存在循环依赖关系。我该如何解决?

错误:对不完整类型“Face”的‘sizeof’无效应用

class Face; // needs to be incomplete

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

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

1
你可能想阅读这篇文章:http://home.roadrunner.com/~hinnant/incomplete.html - shuttle87
@stefan:看看类型之间的循环依赖关系。虽然这个原因可能在问题中已经被强调了。 - Csq
你可以在共享指针和裸指针中使用不完整类型。 - OMGtechy
1
你收到了什么错误?我在我的mac上编译了你的代码,没有收到任何错误。 - Spundun
你使用哪个版本的gcc编译哪段代码时出现了什么错误?我无法复现... - Marc Glisse
@MarcGlisse:我在使用gcc版本4.8.2时遇到了帖子中提到的错误。 - Shibli
1个回答

21
一种解决方法是在Element和Face的声明中声明析构函数和构造函数,但不在头文件中定义它们。然后你需要在cpp文件中定义它们。
(更多技术细节可以在我的问题答案中找到:Is std::unique_ptr<T> required to know the full definition of T?
问题的根源在于unique_ptr的析构函数需要调用delete(默认情况下),因此它需要知道类型的定义(以获得其大小)。但是,如果Element和Face的析构函数是自动生成的,则默认情况下将被内联:使用Element和Face实例的代码将被强制知道两种类型的大小,以便它们的析构函数可以调用unique_ptr的析构函数,后者可以使用与指针关联的类型调用delete。
我提供的解决方案将确保unique_ptr的构建和销毁在单独的cpp中定义。它们不会被内联,但仍可被使用Element和Face的代码调用。unique_ptrs的析构函数代码将在定义Element和Face的析构函数的cpp中,因此在这些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; 

如果它们在不同的头文件/源文件中,解决方案仍然相同。但是,您需要进行更多的前向声明,并且定义构造/析构的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; 

如果FaceElement有它们自己的头文件和cpp文件呢? - Shibli
@Shibli 同样的答案,但是你只需要为Face前向声明Element,为Element前向声明Face。我会添加一个例子。 - Klaim
1
确实如此。但是为什么内联它们会成为一个问题呢? - Gilgamesz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接