2017-08-16 69 views
0

我来到了同一个问题在我们的代码库这里
Can't allocate class with forward declared value in std::map member variable
描述。不完全类型为std ::成员地图

Hoever我还发现其他情况下,我们的编译器(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文件定义CON组析构函数&允许构件-STD ::使用的地图变量完整的类型?

+2

因为当你放置'struct Incomplete {};'上面的构造器/析构函数定义时,'incomplete'就完成了。 – VTT

+1

*在cpp *中定义了con&destructor而不是什么? – stijn

+0

它看起来像你“发现”,如果你在接口文件中命名一个方法......甚至是一个析构函数......那么它需要在你的实现文件中定义。接口中的内联方法在实现中不需要定义。看起来像适合我的设计。 –

回答

2

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

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

但是,如果你告诉编译器(由Test构造函数/析构函数声明的方式),其稍后将实现它们,那么就不会产生过的,所以没有std::map构造函数/析构函数调用被编译时那一点。

然后你完成Incomplete类型定义构造函数/析构函数自己之前,所以Incomplete构造函数/析构函数调用可以编译成功。如果您删除了Incomplete的定义,那么您将遇到相同的错误。


需要注意的是,正如其他人所说,你可以通过存储在地图,而不是不完全类型的指针/引用避开这个问题。对不完整类型的指针或引用实际上本身就是一个完整类型。然而,这可能不是所有情况下都需要的,所以我不愿意推动该解决方案而不知道更多关于如何使用地图的细节。