作为 std::map 成员的不完整类型

incomplete type as member of std::map

我遇到了与此处所述相同的问题
Can't allocate class with forward declared value in std::map member variable
在我们的代码库中。

但是我还发现了我们的编译器 (MSVC 2017) 能够编译这个的其他情况...
在摆弄代码后,我发现在 cpp 中定义 con- 和析构函数允许文件编译。

test.h中:

#ifndef TEST_H
#define TEST_H

#include <map>
struct Incomplete;

class Test {
    std::map<int, Incomplete> member;
public:
    Test();
    ~Test();
    int foo() { return 0; }
};

#endif

test.cpp中:

#include "test.h"
struct Incomplete {};
Test::Test() {}
Test::~Test() {}

main.cpp中:

#include "test.h"

int main() 
{
    Test test;
    return test.foo();
}

为什么在cpp文件中定义构造函数和析构函数允许member-std::map-variables使用不完整的类型?

这是因为声明 class 成员不需要 Incomplete 完成,但是 调用 std::map 析构函数需要 ,因为它必然需要调用 Incomplete 析构函数来销毁地图内容。 (调用默认 std::map 构造函数 可能 需要类型完整,具体取决于实现。我不确定规范是否对此提出任何要求。我可以认为至少一种不需要完整类型的实现。)

如果你依赖编译器为你生成隐式ctors/dtors,那意味着遇到class定义时类型必须是完整的,因为那是编译器要隐式生成ctor和dtor。就好像您在 class 定义之后立即写了 inline Test::Test() {} inline Test::~Test() {} 一样。 dtor 将隐式销毁映射,这将通过对任何存储值调用 ~Incomplete() 来销毁映射内容,如果没有 Incomplete 的定义,我们将无法做到这一点。在那里,整个事情都崩溃了,你得到了一个错误。

但是,如果您告诉编译器(通过 Test ctor/dtor 声明) 将在稍后实现它们,那么它将获胜'不生成它们,因此没有 std::map ctor/dtor 调用在那个时候被编译。

然后您在自己定义 ctor/dtor 之前完成 Incomplete 类型,因此 Incomplete ctor/dtor 调用 可以 编译成功。如果您删除 Incomplete 的定义,那么您将 运行 陷入同样的​​错误。


请注意,正如其他人所说,您可以通过将 pointers/references 存储到地图中的不完整类型来回避这个问题。指向不完整类型的指针或引用实际上本身就是一个完整类型。但是,这可能并非在所有情况下都可取,因此我在不知道有关如何使用地图的更多详细信息的情况下犹豫是否要推出该解决方案。