我有一个小问题,我现在不明白,也找不到解释。我阅读了有关如何在 PIMPL 惯用语中使用 std::unique_ptr 的信息,它可以工作,但..不是在一种奇怪的情况下,我现在当然会想到这种情况。
最简单的 - 我将展示一个重现问题的简化代码示例(使用 VS2017 社区编译)。
header.h ##
Forward类的前向声明,以及具有返回unique_ptr的虚函数的模板类TestForward。
class Forward;
using TestUniquePtr = std::unique_ptr<Forward>;
TestUniquePtr make_ptr();
template<int a>
class TestForward {
public:
virtual TestUniquePtr foo();
};
template<int a>
TestUniquePtr TestForward<a>::foo() {
return make_ptr();
}
转发.h
#include "header.h"
#include <iostream>
class Forward {
public:
~Forward() {
std::cout << "HAAA" << std::endl;
}
};
转发.cpp
#include "forward.h"
TestUniquePtr make_ptr() {
return TestUniquePtr{ new Forward };
}
main.cpp
由于“无法删除不完整的类型”而无法编译的文件。
请注意,这里甚至没有调用函数 foo
。
那么编译器应该尝试在这个单元中编译这个函数吗?
如果此函数不是虚拟函数或 TestForward 不是模板 - 它可以工作。
#include "header.h"
int main (int argc, char *argv[]) {
TestForward<3> a;
return 0;
}
我知道我该如何解决这个问题 - 通过定义我的删除器,它不是模板,并将其定义写在 forward.cpp 中但是..我认为这应该有效,所以请帮助我找出为什么模板 + 虚拟使它成为不工作:(
最佳答案
这里发生了太多事情,所有这些都导致了这个错误......
首先,考虑一下:C++ 标准规定如果您这样做:
struct Incomplete;
void foo(Incomplete* p) { delete p; }
这是合法的,但如果 Incomplete
的完整定义原来有一个非平凡的析构函数,程序有未定义的行为。我认为这仅仅是为了与早期的类 C C++ 程序兼容。
所以,为了提高程序的安全性,unique_ptr
的默认删除器使用“安全删除”,即无法针对不完整类型进行编译的删除。这意味着 unique_ptr
的实例化析构函数必须知道指向类的完整定义。
在您的程序中,任何使用 TestUniquePtr
的代码因此析构函数必须知道 Forward
的完整定义。 .
TestForward::foo
使用析构函数。 make_ptr
返回一个对象。 foo
move-从此对象构造自己的返回值,然后销毁源。 (在实际生成的代码中,这很可能被返回值优化优化掉了,但没有它代码仍然有效。)
哪里/为什么是TestForward<3>::foo
用过的?好吧,既然是虚的,那类的vtable在哪里实例化的地方就必须实例化。并且由于是模板实例化,所以在调用构造函数的任何地方都会实例化vtable(因为构造函数需要将vtable指针写入对象)。在 main
中调用了构造函数.
如果foo
不是虚拟的,没有必要实例化它。如果TestForward
不是模板,我猜你放了foo
进入一些单独的源文件而不是标题,因此错误没有出现 main
.
那么如何解决这个问题呢?
在典型的 Pimpl 上下文中,您可以通过严格控制谁实例化 unique_ptr
的析构函数来解决此问题.您显式声明接口(interface)类的析构函数并将定义放入已知 impl 类定义的源文件中。
但是,如果您想分发 unique_ptr
s 到你不完整的类作为不透明句柄,你需要替换默认删除器。
// header.h
class Forward;
struct ForwardDeleter {
void operator ()(Forward* ptr);
};
using TestUniquePtr = std::unique_ptr<Forward, ForwardDeleter>;
// forward.cpp
class Forward { ... };
void ForwardDeleter::operator ()(Forward* ptr) { delete ptr; }
关于c++ - std::unique_ptr 虚函数模板类中的不完整类型错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46484255/