将空基 class 指针转换为子 class 指针?
Cast empty base class pointer to child class pointer?
我有一些代码使用了从基本 class 类型到子 class 类型的有点偷偷摸摸的转换,其中子 class 类型被指定为模板范围。我假设因为 base class 没有声明数据成员并且大小为零,所以 base class 指针地址将与子指针地址相同,并且转换将成功。到目前为止代码 运行s 正确。
这是我正在做的事情的简化版本:
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> {
// ...
};
我的问题基本上是 - 代码是否符合犹太洁食标准?如果基为空,基指针是否总是等于子指针?它依赖于实现吗?以后我 运行 会惹上麻烦吗?
它很好地解决了我的问题,但我有点怀疑。
基数 class 不需要为空。
只要基是可访问的、明确的、非虚拟的,并且指向基的指针实际指向派生对象的基子对象,就可以有效地执行 static_cast
从指向派生指针的指向基的指针。编译器将对指针值执行任何必要的调整。
您所做的实际上是一种常见的模式,称为奇怪的重复模板模式(或 CRTP)。
reinterpret_cast
是一个完全不同的野兽,但是,在这种情况下,它可能是一张通往未定义行为领域的单程票(我懒得深入研究标准并弄清楚在哪个下极端情况不是 UB)。由于 C 风格的转换可以变成 reinterpret_cast
(在这里,如果您不小心从 Base<Baz>
派生了 Foo
而 Baz
不是从 Base<Baz>
派生的) , 它不应该被使用。
此外,还有 可确保您不会意外地从 Base<Bar>
派生出 Foo
- 仅使用 static_cast
不会阻止 if Bar
也派生自 Base<Bar>
.
这是 CRTP。
使用 static_cast<Derived*>(this)
更安全,因为 C 风格的转换在某些情况下会是 "too strong".
使用 Foo<>
作为 CRTP 基础 class:
只要有问题的 Foo<Derived>
实际上是 Derived
的基础,那么 static_cast<Derived*>(this)
在运行时是安全的并且做正确的事情。
static_assert
of is_base_of
可以减少此处可能的错误来源之一,但不是全部。如果 struct Bar : Foo<Derived>
上面的转换有未定义的行为,我知道没有办法检查那个错误。
我有一些代码使用了从基本 class 类型到子 class 类型的有点偷偷摸摸的转换,其中子 class 类型被指定为模板范围。我假设因为 base class 没有声明数据成员并且大小为零,所以 base class 指针地址将与子指针地址相同,并且转换将成功。到目前为止代码 运行s 正确。
这是我正在做的事情的简化版本:
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> {
// ...
};
我的问题基本上是 - 代码是否符合犹太洁食标准?如果基为空,基指针是否总是等于子指针?它依赖于实现吗?以后我 运行 会惹上麻烦吗?
它很好地解决了我的问题,但我有点怀疑。
基数 class 不需要为空。
只要基是可访问的、明确的、非虚拟的,并且指向基的指针实际指向派生对象的基子对象,就可以有效地执行 static_cast
从指向派生指针的指向基的指针。编译器将对指针值执行任何必要的调整。
您所做的实际上是一种常见的模式,称为奇怪的重复模板模式(或 CRTP)。
reinterpret_cast
是一个完全不同的野兽,但是,在这种情况下,它可能是一张通往未定义行为领域的单程票(我懒得深入研究标准并弄清楚在哪个下极端情况不是 UB)。由于 C 风格的转换可以变成 reinterpret_cast
(在这里,如果您不小心从 Base<Baz>
派生了 Foo
而 Baz
不是从 Base<Baz>
派生的) , 它不应该被使用。
此外,还有 Base<Bar>
派生出 Foo
- 仅使用 static_cast
不会阻止 if Bar
也派生自 Base<Bar>
.
这是 CRTP。
使用 static_cast<Derived*>(this)
更安全,因为 C 风格的转换在某些情况下会是 "too strong".
使用 Foo<>
作为 CRTP 基础 class:
只要有问题的 Foo<Derived>
实际上是 Derived
的基础,那么 static_cast<Derived*>(this)
在运行时是安全的并且做正确的事情。
static_assert
of is_base_of
可以减少此处可能的错误来源之一,但不是全部。如果 struct Bar : Foo<Derived>
上面的转换有未定义的行为,我知道没有办法检查那个错误。