std::vector 的不完整类型

Posted

技术标签:

【中文标题】std::vector 的不完整类型【英文标题】:Incomplete type for std::vector 【发布时间】:2014-07-21 22:51:59 【问题描述】:

当我尝试以下操作时,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;
;

【问题讨论】:

您可能想阅读以下内容:home.roadrunner.com/~hinnant/incomplete.html @stefan :查看类型之间的循环依赖关系。不过,这个原因可能已在问题中突出显示。 您可以将不完整类型与共享指针和原始指针一起使用。 您遇到的错误是什么?我在我的 mac 上编译了你的代码,我没有收到任何错误。 你在什么版本的 gcc 和什么代码上遇到了什么错误?我无法复制... 【参考方案1】:

解决此问题的一种方法是声明 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; 

如果它们在不同的 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; 

【讨论】:

如果FaceElement有自己的头文件和cpp文件怎么办? @Shibli 相同的答案,但是你只需要前向声明 Face 的 Element 和 Element 的 Face 。我将添加一个示例。 "但是如果Element和Face的析构函数是自动生成的,那么默认是内联的:t" 是的,确实。但是为什么它们是内联的呢?

以上是关于std::vector 的不完整类型的主要内容,如果未能解决你的问题,请参考以下文章

为啥 std::vector 的速度是原始数组的两倍?包含完整代码

作为函数参数和返回值的不完整类型

GMOCKing 接口时 std::any 的不完整类型

具有 std::map 和 std::variant 的不完整类型

C++ std::vector 乘法中是不是存在已知的不一致行为?

g++5 中 std::unordered_set 编译错误的不完整类型,在 clang++ 中编译