[我试图找到一种方法来静态断言,当遇到意外情况时,派生类指针可以安全地重新解释为指向基类的指针:
我本以为以下代码中的两个静态断言会失败。
class Base1 {
int x;
};
class Base2 {
int y;
};
class Derived : public Base1, public Base2 {
int z;
};
// one of these should fail???
static_assert( (Derived *)nullptr == (Base1 *)nullptr, "err1" );
static_assert( (Derived *)nullptr == (Base2 *)nullptr, "err2" );
// and one of these as well???
static_assert( (Base1 *)(Derived *)nullptr == nullptr, "err3" );
static_assert( (Base2 *)(Derived *)nullptr == nullptr, "err3" );
对于第一对断言,由于其参数的类型为Derived*
和Base1*
/ Base2*
,因此我希望Derived*
会隐式转换为Base1*
或Base2*
转换。由于Base1
和Base2
不能占用相同的内存,因此它们不能都位于Derived
对象所占用的内存的开头,因此这些转换之一应该增加了指针值。那么就不应该发现非零指针等于null。
类似地,对于第二对,我希望显式转换为Base1*
,并且Base2*
应该已经改变了其中一个指针,但它们仍被比较为等于null。
这里发生了什么?
由于将[conv.ptr](强调我的意思)
3类型为“指向cvD
的指针”的prvalue,其中D
是一个类类型,可以转换为“指向cvB
的指针”类型的prvalue,其中B
是D
的基类。如果B
不可访问或不明确D
的基类,需要进行此转换的程序是格式错误。转换的结果是指向基数的指针派生类对象的class子对象。空指针值转换为目标类型的空指针值
。
Derived*
与Base*
进行比较需要将它们转换为通用类型,因此将发生转换。如您所见,它是空值保留。这样做的理由是,应该不可能从空指针值生成有效的查找值。如果您以空指针值开头,则将继续使用它。
让我们回顾以下引用:
当遇到C样式强制转换表达式时,编译器尝试按以下顺序将其解释为以下强制转换表达式:a)const_cast<new_type>(expression)
b)static_cast<new_type>(expression)
如果尝试使用reinterpret_cast
,请使用it will fail compilation,因为reinterpret_cast
不是常量表达式,但是static_cast
的作用不只是为类型系统强制转换。在这种情况下,static_cast
可能会告诉编译器根据Base1
,Base2
和Derived
的大小(在编译时已知)执行一些偏移,但是static_cast may involve implicit conversions, a call to the constructor of new_type or a call to a user-defined conversion operator.
此外,static_cast
中的文本还显示以下内容:
2)如果new_type是指向某个类D的指针或引用,而表达式的类型是指向其非虚拟基数B的指针或引用,则static_cast执行向下转换。如果B是模棱两可,不可访问或D的虚拟基数(或虚拟基数的基数),则此向下转换格式错误。9)指向某些类D的成员的指针可以向上转换为的指针。其明确,可访问的基类B的成员。此static_cast不进行检查以确保成员在运行时中实际存在指向对象的类型。
这实际上是因为Derived
的布局是完整的,并且尽管此布局在技术上是实现定义的,但编译器准确地知道此布局,因为Base1
和Base2
都是不可访问的或虚拟的。
我们实际上可以通过提供无法访问的基数来查看失败的情况:
class Base{};
class Base1 : public Base
{
int x;
};
class Base2 : public Base
{
int y;
};
class Derived
: public Base1
, public Base2
{
int z;
};
constexpr static Derived d{};
constexpr static const Derived* derived_ptr = &d;
constexpr static const Base1* base1_ptr = &d;
constexpr static const Base2* base2_ptr = &d;
// fail due to inaccessible base
static_assert(static_cast<const Derived*>(nullptr) == static_cast<const Base*>(nullptr), "err1" ); // fails
static_assert(static_cast<const Derived*>(derived_ptr) == static_cast<const Base*>(nullptr), "err2" ); // fails
// succeed
static_assert(static_cast<const Derived*>(derived_ptr) == static_cast<const Base1*>(base1_ptr), "err3" );
static_assert(static_cast<const Derived*>(derived_ptr) == static_cast<const Base2*>(base2_ptr), "err4" );
// and one of these as well???
static_assert(static_cast<const Base1*>(static_cast<const Derived*>(derived_ptr)) == base1_ptr, "err5" );
static_assert(static_cast<const Base2*>(static_cast<const Derived*>(derived_ptr)) == base2_ptr, "err6" );