为什么在类型转换的nullptr上不进行隐式转换

问题描述 投票:1回答:2

[我试图找到一种方法来静态断言,当遇到意外情况时,派生类指针可以安全地重新解释为指向基类的指针:

我本以为以下代码中的两个静态断言会失败。

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*转换。由于Base1Base2不能占用相同的内存,因此它们不能都位于Derived对象所占用的内存的开头,因此这些转换之一应该增加了指针值。那么就不应该发现非零指针等于null。

类似地,对于第二对,我希望显式转换为Base1*,并且Base2*应该已经改变了其中一个指针,但它们仍被比较为等于null。

这里发生了什么?

c++ multiple-inheritance static-assert
2个回答
1
投票
空指针始终是空指针,并且保持为空指针。

[conv.ptr](强调我的意思)

3类型为“指向cv D的指针”的prvalue,其中D是一个类类型,可以转换为“指向cv B的指针”类型的prvalue,其中BD的基类。如果B不可访问或不明确D的基类,需要进行此转换的程序是格式错误。转换的结果是指向基数的指针派生类对象的class子对象。

空指针值转换为目标类型的空指针值

由于将Derived*Base*进行比较需要将它们转换为通用类型,因此将发生转换。如您所见,它是空值保留。

这样做的理由是,应该不可能从空指针值生成有效的查找值。如果您以空指针值开头,则将继续使用它。


0
投票
输入C-style cast

让我们回顾以下引用:

当遇到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可能会告诉编译器根据Base1Base2Derived的大小(在编译时已知)执行一些偏移,但是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的布局是完整的,并且尽管此布局在技术上是实现定义的,但编译器准确地知道此布局,因为Base1Base2都是不可访问的或虚拟的。

我们实际上可以通过提供无法访问的基数来查看失败的情况:

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" );

© www.soinside.com 2019 - 2024. All rights reserved.