问题描述
我已经在此处和,但仍然无法找出问题所在.
I already checked out the questions here and here, but still cannot figure out what is wrong.
这是调用代码:
#include "lib.h"
using namespace lib;
int
main(const int argc, const char *argv[])
{
return 0;
}
这是lib代码:
#ifndef lib_h
#define lib_h
#include <string>
#include <vector>
#include <memory>
namespace lib
{
class Foo_impl;
class Foo
{
public:
Foo();
~Foo();
private:
Foo(const Foo&);
Foo& operator=(const Foo&);
std::unique_ptr<Foo_impl> m_impl = nullptr;
friend class Foo_impl;
};
} // namespace
#endif
clang ++给我这个错误:
clang++ gives me this error:
您可以看到我已经明确声明了Foo析构函数.我在这里还想念什么?
You can see I already specifically declared Foo destructor. What else am I missing here?
推荐答案
Foo_impl
的实现必须在std::unique_ptr<Foo_impl> m_impl = nullptr
中要求的实例化之前完成.
The implementation of Foo_impl
must be complete prior to the instantiation required in std::unique_ptr<Foo_impl> m_impl = nullptr
.
保留声明的类型(但不进行初始化)将解决错误(std::unique_ptr<Foo_impl> m_impl;
),然后稍后需要在代码中对其进行初始化.
Leaving the type declared (but not initialised) will fix the error (std::unique_ptr<Foo_impl> m_impl;
), you would then need to initialise it later on in the code.
您看到的错误来自用于测试此技术的技术的实现;不完整的类型.基本上,sizeof
会导致仅向前声明的类型产生错误(即,在代码/编译时在该点使用时缺少定义).
The error you are seeing is from the implementation of a technique used to test for this; the incomplete type. Basically, sizeof
will result in an error with types that are only forward declared (i.e. lack definition when used at that point in the code/compilation).
这里可能的解决方法如下;
A possible fix here would look like;
class Foo_impl;
class Foo
{
// redacted
public:
Foo();
~Foo();
private:
Foo(const Foo&);
Foo& operator=(const Foo&);
std::unique_ptr<Foo_impl> m_impl;// = nullptr;
};
class Foo_impl {
// ...
};
Foo::Foo() : m_impl(nullptr)
{
}
为什么需要完整的类型?
通过= nullptr
实例化使用复制初始化,并要求构造函数和要声明的析构函数(对于unique_ptr<Foo_impl>
).析构函数需要unique_ptr
的deleter函数,默认情况下,该函数在指向Foo_impl
的指针上调用delete
,因此它需要析构函数Foo_impl
,并且Foo_impl
的析构函数未在不完整的声明中声明.类型(编译器不知道它是什么样子).另请参见霍华德的答案.
The instantiation via = nullptr
uses copy initialisation and requires the constructor and destructor to be declared (for unique_ptr<Foo_impl>
). The destructor requires the deleter function of the unique_ptr
which, by default, calls delete
on the pointer to Foo_impl
hence it requires the destructor of Foo_impl
, and the destructor of Foo_impl
is not declared in the incomplete type (the compiler doesn't know what it looks like). See Howard's answer on this as well.
关键是在不完整类型上调用delete
会导致未定义的行为(第5.3.5/5节),因此在unique_ptr
的实现中进行了明确检查.
Key here is that calling delete
on an incomplete type results in undefined behaviour (§ 5.3.5/5) and hence is explicitly checked for in the implementation of unique_ptr
.
这种情况的另一种替代方法可能是使用直接初始化,如下所示;
Another alternative for this situation may be to use direct initialisation as follows;
std::unique_ptr<Foo_impl> m_impl { nullptr };
关于非静态数据成员初始化程序(NSDMI)以及至少在这是否是需要成员定义的上下文,似乎存在一些争论.对于clang(可能还有gcc),这似乎就是这样的上下文.
There seems to be some debate on the non-static data member initialiser (NSDMI) and whether this is a context that requires the member definition to exist, at least for clang (and possibly gcc), this seems to be such a context.
这篇关于unique_ptr,pimpl/forward声明和完整定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!