Sne*_*tel 5 c++ incomplete-type template-instantiation
根据一个简单的,侵入式引用计数的对象系统,我有一个template<typename T> class Handle
,它意味着用子类实例化CountedBase
.Handle<T>
拥有一个指向a的指针T
,并且它的析构函数在该指针上调用DecRef
(定义CountedBase
).
通常,这会在尝试使用前向声明限制标头依赖性时导致问题:
#include "Handle.h"
class Foo; // forward declaration
struct MyStruct {
Handle<Foo> foo; // This is okay, but...
};
void Bar() {
MyStruct ms;
} // ...there's an error here, as the implicit ~MyStruct calls
// Handle<Foo>::~Handle(), which wants Foo to be a complete
// type so it can call Foo::DecRef(). To solve this, I have
// to #include the definition of Foo.
Run Code Online (Sandbox Code Playgroud)
作为解决方案,我重写Handle<T>::~Handle()
如下:
template<typename T>
Handle<T>::~Handle() {
reinterpret_cast<CountedBase*>(m_ptr)->DecRef();
}
Run Code Online (Sandbox Code Playgroud)
请注意,我在reinterpret_cast
这里使用而不是static_cast
,因为reinterpret_cast
不需要T
完成定义.当然,它也不会为我执行指针调整...但只要我对布局很谨慎(T
必须有CountedBase
最左边的祖先,不能虚拟地继承它,并且在几个不寻常的平台上,一些额外的vtable魔法是必要的),这是安全的.
但是,如果我可以在可能的情况下获得额外的安全层,那将会是非常好的static_cast
.在实践中,该定义T
是通常在哪里点完全Handle::~Handle
被实例化,使其成为一个完美的时刻重复检查T
实际上是从继承CountedBase
.如果它不完整,那我就无能为力......但如果它完成了,那么理智检查就会很好.
这给我们带来了,终于,我的问题:
有没有办法做一个编译时的支票,T
从继承CountedBase
,这将不会导致(假)错误时T
不完整?
[通常的免责声明:我知道以这种方式使用不完整类型可能存在不安全和/或UB方面的问题.然而,经过大量的跨平台测试和分析后,我已经确定这是我用例的某些独特方面最实用的方法.我对编译时检查问题很感兴趣,而不是一般的代码审查.]
使用 SFINAE onsizeof
检查类型是否完整:
struct CountedBase {
void decRef() {}
};
struct Incomplete;
struct Complete : CountedBase {};
template <std::size_t> struct size_tag;
template <class T>
void decRef(T *ptr, size_tag<sizeof(T)>*) {
std::cout << "static\n";
static_cast<CountedBase*>(ptr)->decRef();
}
template <class T>
void decRef(T *ptr, ...) {
std::cout << "reinterpret\n";
reinterpret_cast<CountedBase*>(ptr)->decRef();
}
template <class T>
struct Handle {
~Handle() {
decRef(m_ptr, nullptr);
}
T *m_ptr = nullptr;
};
int main() {
Handle<Incomplete> h1;
Handle<Complete> h2;
}
Run Code Online (Sandbox Code Playgroud)
输出(注意破坏的顺序是相反的):
static
reinterpret
Run Code Online (Sandbox Code Playgroud)
尝试使用不是从CountedBase
Yield 派生的完整类型:
main.cpp:16:5: error: static_cast from 'Oops *' to 'CountedBase *' is not allowed
Run Code Online (Sandbox Code Playgroud)
话虽这么说,我认为更优雅(也更明确)的方法是引入一个类模板incomplete<T>
,这样就可以Handle<incomplete<Foo>>
编译为reinterpret_cast
,而其他任何东西都会尝试编译为static_cast
。
归档时间: |
|
查看次数: |
100 次 |
最近记录: |