根据一个简单的、侵入式引用计数的对象系统,我有一个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.
作为解决方案,我重写Handle<T>::~Handle()
如下:
template<typename T>
Handle<T>::~Handle() {
reinterpret_cast<CountedBase*>(m_ptr)->DecRef();
}
请注意,我在reinterpret_cast
这里使用而不是static_cast
,因为reinterpret_cast
不需要T
完整的定义。当然,它也不会为我执行指针调整......但只要我小心布局(T
必须有CountedBase
它最左边的祖先,不能虚拟继承它,并且在几个不寻常的平台上,一些额外的 vtable 魔法是必要的),它是安全的。
不过,如果我能在可能的情况下获得额外的安全层,那就太好了static_cast
。在实践中,定义T
通常在被实例化的时候就完成了,Handle::~Handle
这使它成为仔细检查T
实际继承自CountedBase
. 如果它不完整,我无能为力……但如果它是完整的,那么进行健全性检查会很好。
最后,这给我们带来了我的问题:
是否有任何方法可以进行编译时检查,当未完成T
时CountedBase
不会导致(虚假)错误?T
[通常的免责声明:我知道以这种方式使用不完整类型存在潜在的不安全和/或 UB 方面。尽管如此,经过大量跨平台测试和分析后,鉴于我的用例的某些独特方面,我确定这是最实用的方法。我对编译时检查问题感兴趣,而不是一般的代码审查。]