【问题标题】:reinterpret_cast integer to pointer with upcastreinterpret_cast 整数到向上转换的指针
【发布时间】:2016-12-26 16:12:19
【问题描述】:

以下代码安全吗:

#include <iostream>
#include <cstdint>

struct A{
  int i = 0;
  virtual int foo() {return i;}
};
struct B : A{
  int foo() override {return i+2;}
};

using handle_t = std::uintptr_t;
handle_t get(B& a){
  return reinterpret_cast<handle_t>(&a);
}

void use(handle_t h){
  auto p= reinterpret_cast<A*>(h); // 
  std::cout << p->foo() << "\n";
}

int main(int argc, char *argv[])
{
  B a;
  auto h = get(a);
  use(h);
  return 0;
}

CppReference's page 说可以:

  • 将 B* 重新解释为 std::uintptr_t
  • reinterpret_cast 从 std::uintptr_t 到 B*(因为它来回是同一类型)
  • 将 B* 重新解释为 A*

那么,合并最后两个是否安全?

【问题讨论】:

  • 我看不到 cppreference 页面说您可以使用 reinterpret_cast 进行向上转换,正如您声称的“从 B* 到 A* 的重新解释”。如果是这样,那就错了。但我认为这里的错误是你的。
  • @Cheersandhth.-Alf 在链接页面 (5) 上说,如果类型别名规则允许,您可以取消引用 reinterpret_cast 的结果,在下面(类型别名规则部分),它说如果 "AliasedType 是 DynamicType 的(可能是 cv 限定的)基类" 是允许的(但我并不声称这符合标准措辞)。
  • 链接已修复。规则 5(尽管 6 似乎也可以)允许向上转换,其中 T1=B* 和 T2=A* 以及下面的类型别名规则。如果 AliasedType 是 DynamicType 的(可能是 cv 限定的)基类,则从 DynamicType 转换为 AliasedType
  • @Davidbrcz:由于很容易产生一个具有 UB 的反例并且会在常见的编译器中崩溃,所以您可以绝对确定 reinterpret_cast 不能用作向上转换。一般来说。最常见的此类示例有 A 作为 POD 类,B 引入了一个虚函数,这可能导致编译器在对象的开头引入一个 vtable 指针。
  • cppreference 更新了适当的指针互转换规则,以添加到派生作为基础的左值访问规则。它可能需要更多的打磨。

标签: c++ casting c++14 reinterpret-cast


【解决方案1】:

在这种特殊情况下,代码是安全的。那是因为 upcasting 的安全性。这对于公共继承总是允许的,没有显式类型转换。

简而言之,您只是强迫某些可以被视为隐含的东西。

B 类型的地址与uintptr_t 的通过也是无用的,但允许,因为“相同类型”。

编辑

关于uintptr_t。

整数类型能够保存从 void 指针转换而来的值,然后再转换回该类型,该类型的值与原始指针的比较值相等。

注意“比较等于原始指针”。

【讨论】:

  • 不,演员表不只是强制一些隐含的东西。它强制执行规则不允许的事情。根据继承的实现方式,隐式转换可能需要对指针的值进行调整,而当强制转换直接转到基类型时,就没有办法做到这一点。
  • @PeteBecker "隐式转换可能需要调整指针的值",我没有意识到,你是什么意思?你能举个例子吗?
  • 派生类型的对象的地址不要求与其任何基的地址相同。对于我所知道的每个编译器,对象的地址与其第一个基址的地址相同(即,它是单继承中的唯一基址),但这是一个实现细节,而不是要求。
【解决方案2】:

我不知道“安全”是什么意思,但是使用此代码的程序的行为是未定义的。您可以将指针转换为足以容纳该值的整数类型,并且可以将该值转换回与原始类型相同的指针。问题中的代码没有这样做:它将值转换为与原始类型不同的指针。

【讨论】:

  • @Davidbrcz - 未定义的行为仅意味着语言定义不会告诉您代码将做什么。这并不意味着坏事一定会发生,只是它们可能会发生。或者他们可能不会。
猜你喜欢
  • 2021-01-22
  • 2021-09-09
  • 1970-01-01
  • 1970-01-01
  • 2021-01-03
  • 2021-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多