对可能不完整的类型进行可选的安全检查强制转换
Optionally safety-checked cast on possibly incomplete type
根据一个简单的、侵入式引用计数的对象系统,我有一个template<typename T> class Handle
,它打算用CountedBase
的一个子类实例化。Handle<T>
持有一个指向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
作为其最左边的祖先,不能虚拟地继承它,并且在一些不寻常的平台上,一些额外的虚表魔法是必要的),它是安全的。
如果我能在可能的情况下获得额外的static_cast
安全层,那么真正的是什么呢?在实践中,T
的定义通常在Handle::~Handle
实例化时就已经完成了,这使得再次检查T
是否确实继承自CountedBase
成为了一个完美的时机。如果它不完整,我就无能为力了……但是如果它是完整的,那么一个完整性检查将是很好的。
这就引出了我的问题:是否有任何方法来做一个编译时检查,T
从CountedBase
继承,当T
不完整时不会导致(虚假)错误?
在sizeof
上使用SFINAE检查类型是否完整:
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 << "staticn";
static_cast<CountedBase*>(ptr)->decRef();
}
template <class T>
void decRef(T *ptr, ...) {
std::cout << "reinterpretn";
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;
}
输出(注意销毁顺序是颠倒的):
static
reinterpret
Live on Coliru
使用不派生自CountedBase
的完整类型进行尝试会得到:
main.cpp:16:5: error: static_cast from 'Oops *' to 'CountedBase *' is not allowed
话虽这么说,我认为更优雅(更显式)的方法是引入一个类模板incomplete<T>
,这样Handle<incomplete<Foo>>
就会编译为reinterpret_cast
,而其他任何东西都试图编译为static_cast
。
- 检查不带转换的扫描格式
- 从值小于256的uint16到uint8的Endian安全转换
- 以线程安全的方式转换 C/C++ 中时区名称字符串的时区偏移量
- 如何将 UTF-8 文本从文件转换为某个可以迭代的容器,并检查每个符号是否为C++字母数字?
- 将"uint8_t"(从套接字读取)隐式转换为"char"安全吗
- 将传入的网络"char*"数据转换为"uint8_t"并返回的安全方法是什么?
- 将 C 函数转换为 C++ 以检查数字是否有效
- 如何检查字符串是否正确以将其转换为 int?
- 从双精度转换为整数的显式类型是否始终检查整数溢出?
- 将字符串转换为整数类型T,检查是否存在溢出
- 积分转换的运行时检查
- 如何在Windows上检查EXE是否具有/GS安全保护
- 有没有一种简单的方法来检查C++中的不安全表达式
- 初始化期间针对安全检查的指针的恒定正确性
- 在转换为较小的数值类型之前执行范围检查的安全、跨平台方法是什么
- 为什么使用static_cast运算符的不安全强制转换不会崩溃
- 在OpenCV 3.1.0 x64 (VS12)中使用TAPI时的BSOD(内核安全检查失败)
- 当数组出界时,是否通过异常安全检查
- 安全,检查后强制转换为c++基类,不需要额外的行
- 对可能不完整的类型进行可选的安全检查强制转换