【问题标题】:Cast empty base class pointer to child class pointer?将空基类指针转换为子类指针?
【发布时间】:2015-03-08 18:29:28
【问题描述】:

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

这是我正在做的简化版本:

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

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

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

【问题讨论】:

  • 你(认为你)为什么需要这个?
  • 我有多种范围类型 - 继承使我无需一遍又一遍地定义相同的 begin() 和 end() 方法。
  • 我的意思是为什么需要强制转换它,而不是为什么需要具有模板类型的迭代器类。
  • 此模式的名称是 CRTP。使用 static_cast,而不是 C 风格的演员表,如果你搞砸了继承,它可能会退化为危险的 reinterpret_cast
  • 注意:你可以写 static_cast&lt;RangeT &amp;&gt;(*this) ,而不是强制转换为指针和解引用

标签: c++ templates inheritance crtp


【解决方案1】:

基类不需要为空。

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

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

reinterpret_cast 是一个完全不同的野兽,然而,在这种情况下,它可能是通往未定义行为领域的单程票(我懒得深入研究标准并弄清楚它是在哪些极端情况下不是UB)。而且由于 C 风格的转换可以变成 reinterpret_cast(这里,如果您在 Baz 不是从 Base&lt;Baz&gt; 派生时意外从 Base&lt;Baz&gt; 派生 Foo),则不应使用它。

此外,还有ways 以确保您不会意外地从Base&lt;Bar&gt; 派生Foo - 如果Bar 也派生自Base&lt;Bar&gt;,则仅使用static_cast 不会阻止。

【讨论】:

    【解决方案2】:

    这是 CRTP。

    使用 static_cast&lt;Derived*&gt;(this) 更安全,因为 C 风格的强制转换在某些情况下会“太强”。

    Foo&lt;&gt; 用于 CRTP 基类:

    只要有问题的Foo&lt;Derived&gt; 实际上是Derived 的基础,那么static_cast&lt;Derived*&gt;(this) 在运行时是安全的并且做正确的事情。

    is_base_of 中的static_assert 可以减少此处可能出现的错误来源,但不是全部。如果struct Bar : Foo&lt;Derived&gt; 上面的演员没有定义的行为,我知道没有办法检查那个错误。

    【讨论】:

    • 你不能检查static_cast的点,但可以确保创建Bar对象不会编译(对于“确保”的某些定义)。
    猜你喜欢
    • 2015-08-12
    • 2013-09-23
    • 1970-01-01
    • 2021-09-20
    • 1970-01-01
    • 2016-10-10
    • 2012-10-24
    • 1970-01-01
    相关资源
    最近更新 更多