【问题标题】:Does the following reinterpret_cast lead to undefined behavior?以下 reinterpret_cast 是否会导致未定义的行为?
【发布时间】:2017-11-04 10:24:19
【问题描述】:

下面代码中的reinterpret_cast 是否会导致未定义的行为?如果是这样,是否可以以类型安全的方式定义rpd

class Base
{ 
public:
  virtual ~Base() = default;
};

class Derived : public Base { };

int main(void)
{
  Derived d;
  Base* pb = &d;
  Base*& rpb = pb;

  Derived*& rpd = reinterpret_cast<Derived*&>(rpb);

  return 0;
}

与我之前的recent question 有点相关。这背后的背景;我正在试验一个适配器类,它应该允许包含协变指针类型的向量本身用作协变类型。

【问题讨论】:

    标签: c++ pointers reinterpret-cast


    【解决方案1】:

    强制转换本身没有 UB(参见 [expr.reinterpret.cast]),但通过重新解释的引用 (rpd) 访问引用的指针 (rpb):

    [basic.lval](标准草案)

    如果程序尝试通过以下类型之一以外的左值访问对象的存储值,则行为未定义56

    56) 此列表的目的是指定对象可能或可能不具有别名的情况。

    • (8.1) 对象的动态类型,

    不适用,动态类型是Base*的静态类型,而不是glvalue的类型Derived*

    • (8.2) 对象动态类型的 cv 限定版本,

    没有 cv-qualifications,类型仍然不匹配。

    • (8.3) 类似于对象的动态类型的类型,

    不适用。这是关于 cv-qualifier 分解,请参阅 [conv.qual](抱歉,段落中的许多下标在输入 html 时很麻烦,并且对于保持文本可读性是必要的)。

    • (8.4) 与对象的动态类型相对应的有符号或无符号类型,

    仅与整数类型有关。

    • (8.5) 对应于对象动态类型的 cv 限定版本的有符号或无符号类型,

    同上。

    • (8.6) 聚合或联合类型,在其元素或非静态数据成员(递归地包括子聚合或包含联合的元素或非静态数据成员)中包含上述类型之一,

    Derived* 既不是聚合也不是联合。

    • (8.7) 一种类型,它是对象动态类型的(可能是 cv 限定的)基类类型,

    Derived* 不是Base* 的基数。

    • (8.8) char、unsigned char 或 std​::​byte 类型。

    Derived* 不是这些。


    由于没有任何例外适用,因此通过 Derived* 类型的左值访问 Base* 的行为是未定义的。


    我正在试验一个适配器类,它应该允许包含协变指针类型的向量本身用作协变类型。

    您的实验将无法坚持基本的面向对象原则。

    基引用与派生是协变的,因为您无法通过基引用对派生对象做任何事情,而派生对象本身无法做到这一点。

    基类型的容器不能与派生的容器协变,因为您可以对基类的容器(通过“引用”容器派生)做一些您不能对派生的容器做的事情:添加对象其他派生类型。

    虽然,如果容器是不可变的......它可能在概念上起作用。实际上在 C++ 中实现它是另一回事。

    【讨论】:

    • 很好的答案,感谢您详细说明标准中的所有要点。
    • 关于协变向量实验的cmets;如果容器不可变,我有一个可行的解决方案。一个限制是,这些容器上的迭代器不会取消引用所包含指针的 const 引用,而是引用所包含指针的(静态转换)副本。我正在寻找解决可变情况的方法。正如您已经明确指出的那样,这是无法做到的。
    • @TonvandenHeuvel 酷!很高兴知道我对不可变对象的直觉是正确的。
    猜你喜欢
    • 2016-09-04
    • 2015-07-30
    • 2021-07-12
    • 1970-01-01
    • 2019-06-03
    • 2016-02-17
    • 2015-01-10
    • 1970-01-01
    • 2014-08-11
    相关资源
    最近更新 更多