为什么在类型转换的nullptr

时间:2019-11-09 00:02:26

标签: c++ multiple-inheritance static-assert

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

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

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。

这是怎么回事?

2 个答案:

答案 0 :(得分:4)

空指针始终是空指针,并且保持为空指针。

  

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

     

may involve implicit conversions, a call to the constructor of new_type or a call to a user-defined conversion operator.类型为“ cv D的指针的prvalue,其中D是一个类   类型,可以转换为类型为“ cv B的指针”的prvalue,其中   BD的基类。如果B不可访问或不明确   D的基类,需要进行此转换的程序是   格式错误。转换的结果是指向基数的指针   派生类对象的class子对象。 空指针值   会转换为目标类型的空指针值。

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

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

答案 1 :(得分: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 { {3}}

此外,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" );