【发布时间】:2021-10-21 21:30:15
【问题描述】:
我正在尝试将 unique_ptr 用于 pimpl 成语。所以我在类中声明了一个析构函数,这样在没有定义impl类的地方就不会实例化unique_ptr删除,然后我在另一个文件中定义它。
这是我的布局:
包装器.h:
#pragma once
#include <memory>
struct Wrapper
{
class Impl;
~Wrapper();
std::unique_ptr<Impl> _impl;
};
包装器.cpp:
#include "wrapper.h"
class Wrapper::Impl {};
Wrapper::~Wrapper() = default;
这个文件编译得很好。 但是,在编译 main.cpp 时出现不完整的类型错误(请参阅下面的错误):
main.cpp:
#include "wrapper.h"
int main()
{
Wrapper w;
return 0;
}
但是,如果我将 wrapper.cpp 中的两行添加到 main.cpp 的末尾,它编译得很好。 我不明白两件事:
- 首先为什么会发生错误?我认为在类中声明析构函数会移动删除调用点?
- 为什么在 main.cpp 文件的末尾添加代码会影响此错误?我虽然“如果使用默认删除器,T 必须是完整的
我错过了什么?
更新:
按照@AdrianMole 的建议,我在类 Wrapper 定义中添加了一个 ctor 声明,并且由于某种原因它修复了错误,即使错误(和 unique_ptr 规范)引用了析构函数。
更新了 wrapper.h:
struct Wrapper
{
class Impl;
Wrapper();
~Wrapper();
std::unique_ptr<Impl> _impl;
};
所以我要添加一个问题:
- 为什么添加构造函数声明可以解决这个问题?
这些是我在使用 MSVC 时遇到的错误,但在 clang 或 gcc 中也会出现类似的错误(我尝试过在线编译器):
memory(2536,1): error C2027: use of undefined type 'Wrapper::Impl'
wrapper.h(7): message : 查看 'Wrapper::Impl' 的声明
memory(2535): message : 在编译类模板成员函数'void std::default_deleteWrapper::Impl::operator ()(_Ty *) noexcept const'时
with [ _Ty=Wrapper::Impl ]memory(2647): message : 请参阅正在编译的函数模板实例化 'void std::default_deleteWrapper::Impl::operator ()(_Ty *) noexcept const' 的引用
with [ _Ty=Wrapper::Impl ]memory(2574): message : 请参阅正在编译的类模板实例化 'std::default_deleteWrapper::Impl' 的引用
wrapper.h(9): message : 请参阅对类模板实例化 'std::unique_ptrWrapper::Impl,std::default_delete<:impl>' 的引用
memory(2536,25):错误 C2338:无法删除不完整的类型
memory(2537,1):警告 C4150:删除指向不完整类型“Wrapper::Impl”的指针;没有调用析构函数
wrapper.h(7): message : 查看 'Wrapper::Impl' 的声明
【问题讨论】:
-
你不应该在
wrapper.cpp中也有Wrapper::Wrapper() : _impl(new Impl) {}吗? -
@m88 编译不需要,所以省略了构造代码。无论如何,在现实生活中的用例中,它不是在构造函数中完成,而是在接收到远程参数之后完成。
-
问题不在于你定义
Wrapper析构函数的位置,而是你定义Impl类的位置。我猜std::unique_ptr<Impl>对象的默认构造函数需要知道Impl的完整定义。 -
@AdrianMole 谢谢,但实际上 unique_ptr 不需要在默认构造函数上完全实现该类型(问题中的 cppreference 链接中的更多详细信息)。并且它没有解释为什么在代码修复它之后添加定义。
-
很好奇。 clang-cl 编译器给出:error : invalid application of 'sizeof' to an不完整类型'Wrapper::Impl' ... 在成员函数'std::unique_ptr::~unique_ptr' 在这里请求 (所以我猜这是
unique_ptr对象的默认析构函数。)但是是的 - 不知道为什么在代码解决了这个问题。
标签: c++ compiler-errors pimpl-idiom