【发布时间】:2016-06-26 01:47:23
【问题描述】:
有一个很好的小技巧here 允许使用不完整类型的std::unique_ptr。
以下是相关代码:
// File: erasedptr.h
#include <memory>
#include <functional>
// type erased deletor (an implementation type using "veneer")
template <typename T>
struct ErasedDeleter : std::function<void(T*)>
{
ErasedDeleter()
: std::function<void(T*)>( [](T * p) {delete p;} )
{}
};
// A unique_ptr typedef
template <typename T>
using ErasedPtr = std::unique_ptr<T, ErasedDeleter<T>>;
// Declare stuff with an incomplete type
struct Foo;
ErasedPtr<Foo> makeFoo();
// File: main.cpp (Foo's definition is not available in this translation unit)
#include "erasedptr.h"
int main() {
ErasedPtr<Foo> f; // [R1]
f = makeFoo();
// ~Foo() gets called fine
}
// File: foo.cpp
#include <iostream>
#include "erasedptr.h"
struct Foo {
~Foo() { std::cout << "~Foo()\n" ; }
};
ErasedPtr<Foo> makeFoo() { return ErasedPtr<Foo>(new Foo); }
这适用于我尝试过的所有编译器:gcc 4.9、clang 3.5 以及 msvc VS13 和 VS15。但它们都会产生以下警告:
deletion of pointer to incomplete type 'Foo'; no destructor called
如果将上面的 [R1] 替换为 ErasedPtr<Foo> f( makeFoo() );,则不会出现警告。
最后,析构函数确实被调用,似乎没有实际问题。该警告是有问题的,因为在质量关键的环境中不能忽略它,并且这种非常有用的模式不可用。
要重现,请创建 3 个文件 erasedptr.hpp、main.cpp、foo.cpp 并编译。
所以问题是:发生了什么?是否有任何替代实现来规避此警告?
【问题讨论】:
-
我认为这只有在
makeFoo可以看到Foo的定义以便ErasedDeleter可以正确调用析构函数时才有效。 -
makeFoo的定义确实看到了Foo的定义(很明显,因为它也是newed)。正如我所指出的,它在这方面工作得很好。问题是默认构造的ErasedPtr<Foo>似乎导致了这个虚假(?)警告。 -
我几乎倾向于认为编译器是正确的,而这种模式根本不安全......
-
尝试使用
virtual析构函数。我的猜测是,这可能会使问题更加明显。但我认为这可能是Delete objects of incomplete type 或Why, really, deleting an incomplete type is undefined behaviour? 的重复项,据我了解,如果Foo具有非平凡的析构函数(在这种情况下确实如此),则这是未定义的行为。 -
这本质上是一个 unique_ptr,无论它维护的对象是否具有虚拟析构函数都是无关紧要的(它可能是一个非类)。删除不完整的类型当然是未定义的,因为没有调用析构函数(内存被释放,但谁知道对象持有的其他资源)。问题不在于删除不完整类型是否有问题。问题是为什么它会发生在这种特定的模式...
标签: c++ c++11 unique-ptr incompatibility incomplete-type