我有一些代码使用从基类类型到子类类型的有点偷偷摸摸的转换,其中子类类型被指定为模板参数。我假设,因为基类声明没有数据成员并且是零大小,基类指针地址将与子项相同,并且转换将成功。到目前为止,代码运行正常。
以下是我正在做的简化版本:
template <class RangeT>
struct CppIterator {
CppIterator(const RangeT& range) { ... }
// ... methods calling RangeT's members
};
// Base class, provides begin() / end() methods.
template<class RangeT>
struct CppIterableBase {
CppIterator<RangeT> begin() {
return CppIterator<RangeT>( *(RangeT*)this ); // Is this safe?
}
CppIterator<RangeT> end() { ... }
};
struct MyRange : CppIterableBase<MyRange> {
// ...
};
我的问题基本上是 - 代码是犹太人吗?如果基数为空,基指针是否总是等于子指针?它是依赖于实现的吗?我以后会遇到麻烦吗?
它很好地解决了我的问题,但我有点怀疑。
答案 0 :(得分:2)
基类不需要为空。
只要基类是可访问的,明确的和非虚拟的,并且指向base的指针实际上指向派生对象的基础子对象,从指针执行static_cast
是有效的。 -to-base to pointer-to-derived。编译器将执行指针值所需的任何调整。
您正在做的事实上是一种常见的模式,称为奇怪的重复模板模式(或CRTP)。
reinterpret_cast
是一个完全不同的野兽,然而,在这种情况下可能是未定义的行为土地的单程票(我懒得深入挖掘标准并弄清楚在哪个角落的情况下它不是UB)。而且,由于C风格的演员阵容可以成为reinterpret_cast
(这里,如果您Foo
在Base<Baz>
未从Baz
派生时意外地从Base<Baz>
派生Foo
,不应该使用它。
此外,还有ways来确保您不会意外地从Base<Bar>
派生static_cast
- 仅使用Bar
无法阻止Base<Bar>
也来自{{1}}。
答案 1 :(得分:2)
这是CRTP。
使用static_cast<Derived*>(this)
会更安全,因为在某些情况下,C风格的演员阵容会过于强大而且#34;
将Foo<>
用于thr CRTP基类:
只要问题中的Foo<Derived>
实际上是Derived
的基础,那么static_cast<Derived*>(this)
在运行时是安全的,并做正确的事。
static_assert
is_base_of
可以在此处减少一个可能的错误来源,但不是全部。如果struct Bar : Foo<Derived>
上面的演员表示未定义的行为,我知道无法检查该错误。