问题
I came about the same issue as described here
Can't allocate class with forward declared value in std::map member variable
in our codebase.
Hoever I also found other cases where our compiler (MSVC 2017) is able to compile this...
After fiddling around with the code I found that defining the con- & destructor in the cpp allows the files to compile.
In 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
In test.cpp
:
#include "test.h"
struct Incomplete {};
Test::Test() {}
Test::~Test() {}
In main.cpp
:
#include "test.h"
int main()
{
Test test;
return test.foo();
}
Why does defining the con- & destructor in the cpp file allow member-std::map-variables to use incomplete types?
回答1:
This is because declaring the class member does not require Incomplete
to be complete, but invoking the std::map
destructor does, because it necessarily needs to invoke the Incomplete
destructor to destroy the map contents. (Invoking the default std::map
constructor could require the type to be complete, depending on the implementation. I'm not sure if the spec puts any requirements on this. I can think of at least one implementation that would not require complete types.)
If you rely on the compiler to generate implicit ctors/dtors for you, that means that the type must be complete when the class definition is encountered, because that's when the compiler is going to implicitly generate the ctor and dtor. It's as though you wrote inline Test::Test() {} inline Test::~Test() {}
immediately following the class definition. The dtor is implicitly going to destroy the map, which is going to destroy the map contents by calling ~Incomplete()
on any stored values, which we can't do without a definition for Incomplete
. And there, the whole thing falls apart and you get an error.
However, if you tell the compiler (by way of the Test
ctor/dtor declarations) that you will be implementing them later, then it won't generate them, therefore no std::map
ctor/dtor invocation gets compiled at that point.
Then you complete the Incomplete
type prior to defining the ctor/dtor yourself, so the Incomplete
ctor/dtor invocations can be successfully compiled. If you remove the definition of Incomplete
then you will run into the same error.
Note that, as others have said, you can side-step this issue by storing pointers/references to the incomplete type in the map instead. A pointer or reference to an incomplete type is actually itself a complete type. However, this may not be desirable in all cases so I'm hesitant to push that solution without knowing more details about how the map will be used.
来源:https://stackoverflow.com/questions/45718599/incomplete-type-as-member-of-stdmap