将空基类指针转换为子类指针?

时间:2015-03-08 18:29:28

标签: c++ templates inheritance crtp

我有一些代码使用从基类类型到子类类型的有点偷偷摸摸的转换,其中子类类型被指定为模板参数。我假设,因为基类声明没有数据成员并且是零大小,基类指针地址将与子项相同,并且转换将成功。到目前为止,代码运行正常。

以下是我正在做的简化版本:

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> {
    // ... 
};

我的问题基本上是 - 代码是犹太人吗?如果基数为空,基指针是否总是等于子指针?它是依赖于实现的吗?我以后会遇到麻烦吗?

它很好地解决了我的问题,但我有点怀疑。

2 个答案:

答案 0 :(得分:2)

基类不需要为空。

只要基类是可访问的,明确的和非虚拟的,并且指向base的指针实际上指向派生对象的基础子对象,从指针执行static_cast是有效的。 -to-base to pointer-to-derived。编译器将执行指针值所需的任何调整。

您正在做的事实上是一种常见的模式,称为奇怪的重复模板模式(或CRTP)。

reinterpret_cast是一个完全不同的野兽,然而,在这种情况下可能是未定义的行为土地的单程票(我懒得深入挖掘标准并弄清楚在哪个角落的情况下它不是UB)。而且,由于C风格的演员阵容可以成为reinterpret_cast(这里,如果您FooBase<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>上面的演员表示未定义的行为,我知道无法检查该错误。