sly*_*sly 6 c++ incompatibility unique-ptr incomplete-type c++11
这里有一个很好的小技巧,允许使用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); }
Run Code Online (Sandbox Code Playgroud)
这适用于我尝试过的所有编译器:gcc 4.9、clang 3.5 以及 msvc VS13 和 VS15。但它们都会生成以下警告:
deletion of pointer to incomplete type 'Foo'; no destructor called
Run Code Online (Sandbox Code Playgroud)
如果将上面的 [R1] 替换为ErasedPtr<Foo> f( makeFoo() );
,则不会出现警告。
最后,析构函数确实被调用,并且似乎没有实际问题。该警告是有问题的,因为在质量关键的环境中不能忽略它,而且这种非常有用的模式不可用。
要重现,请创建 3 个文件erasedptr.hpp
, main.cpp
,foo.cpp
如上所示并编译。
所以问题是:到底发生了什么?是否可以有任何替代实施来规避此警告?
您的问题是有效的,但您尝试使用的代码有点复杂,所以让我停下来寻找想要的解决方案。
~Foo()
,但这只是运气好,这意味着编译器正在做一些奇特的事情。如果您不使用 std::function 但编写自己的自定义删除器,则不会调用析构函数。如果您确实想删除该类型,您可以按以下方式编写/使用 ErasedPtr:
擦除指针.h
// file erasedptr.h
#include <memory>
#include <functional>
// make type erased deleter
template <typename T>
std::function<void(void*)> makeErasedDeleter()
{
return {
[](void* p) {
delete static_cast<T*>(p);
}
};
};
// A unique_ptr typedef
template <typename T>
using ErasedPtr = std::unique_ptr<T, std::function<void(void*)>>;
Run Code Online (Sandbox Code Playgroud)
foo.cpp
// file foo.cpp
#include <iostream>
#include "erasedptr.h."
struct Foo {
~Foo() { std::cout << "~Foo()\n" ; }
};
// capture creation and deletion of Foo in this translation unit
ErasedPtr<Foo> makeFoo() {
return { new Foo, makeErasedDeleter<Foo>() };
}
Run Code Online (Sandbox Code Playgroud)
主程序
// file main.cpp (Foo's definition is not available in this translation unit)
#include "erasedptr.h"
// fwd decl Foo
struct Foo;
ErasedPtr<Foo> makeFoo();
int main() {
ErasedPtr<Foo> f; // [R1]
f = makeFoo();
// ~Foo() gets called fine
}
Run Code Online (Sandbox Code Playgroud)
这样,只有foo.cpp需要知道实际类型,并将删除捕获到std::function
.
您真正想要的是处理不完整的类型。STL 的默认删除器存在的“问题”std::default_delete
是它在编译时断言删除是否安全——而且这是非常正确的!
要使其正常工作,就是使用显式模板实例化告诉编译器/链接器您实际上关心删除的正确实现。这样,您的唯一指针就不需要任何特殊的 typedef/模板别名:
foo.cpp
// file foo.cpp
#include "foo_fwddecl.h"
// capture creation of Foo in this translation unit
std::unique_ptr<Foo> makeFoo() {
return std::make_unique<Foo>();
}
// explicitly instantiate deletion of Foo in this translation unit
template void std::default_delete<Foo>::operator()(Foo*) const noexcept;
template void std::default_delete<const Foo>::operator()(const Foo*) const noexcept;
// note: possibly instantiate for volatile/const volatile modifiers
Run Code Online (Sandbox Code Playgroud)
foo_fwddecl.h
#include <memory>
struct Foo;
std::unique_ptr<Foo> makeFoo();
extern template void std::default_delete<Foo>::operator()(Foo*) const noexcept;
extern template void std::default_delete<const Foo>::operator()(const Foo*) const noexcept;
// note: possibly instantiate for volatile/const volatile modifiers
Run Code Online (Sandbox Code Playgroud)
主程序
// file main.cpp (Foo's definition is not available in this translation unit)
#include "foo_fwddecl.h"
int main() {
std::unique_ptr<Foo> f; // [R1]
f = makeFoo();
// ~Foo() gets called fine
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1768 次 |
最近记录: |