reinterpret_cast转换为void*合法吗?

8
我正在查看 https://en.cppreference.com/w/cpp/language/reinterpret_cast,我注意到它指定了我们始终可以转换的合法类型:

  • byte*
  • char*
  • unsigned char*

但我没有在列表中看到void*,这是一个疏忽吗?我的使用情况需要用reinterpret_castint**转换为void*。最终我会将void*转换回int**


2
"它指定了我们可以始终转换的合法类型" 这似乎是你的发明。那个页面没有说类似的话。 - cpplearner
1
不需要使用 reinterpret_cast 将指针转换为 void*。https://godbolt.org/z/-eIfjl - Max Langhof
我重新打开了这个问题,因为我没有看到只有顶部答案适用于此。在这里留下一个链接:https://dev59.com/N3RB5IYBdhLWcg3wn4UL - NathanOliver
@cpplearner 类型在类型别名部分中。 - Jonathan Mee
@FrançoisAndrieux 呃,我知道。我就像个傻瓜一样忘了。你能发一个回答吗?我会接受的。 - Jonathan Mee
@FrançoisAndrieux 我对你撒谎了。感谢您的发帖。我给了你一个+1,但是Serge Ballesta的回答对我来说更加明确,所以我接受了那个答案。 - Jonathan Mee
3个回答

6
那些类型不受严格别名规则的限制。这并不意味着它们是您可以与 reinterpret_cast 一起使用的唯一类型。在将对象指针转换为另一个对象指针类型的情况下,未能满足严格别名规则的要求意味着您无法安全地引用结果。但是您仍然可以安全地将结果指针强制转换回原始类型,并将结果用作原始指针。
关于reinterpret_cast ,cppreference 上的相关部分如下:
(任何对象指针类型 T1* 都可以转换为另一个对象指针类型 cv T2*)。这相当于 static_cast(static_cast(expression)) (这意味着如果 T2 的对齐要求不比 T1 更严格,则指针的值不会更改,并且将结果指针转换回其原始类型的转换将产生原始值)。在任何情况下,只有在类型别名规则允许时才能安全地引用结果指针
当强制转换回原始类型时,AliasedType 和 DynamicType 是相同的,因此它们是别名规则列出的第一种情况,其中合法引用 reinterpret_cast 结果的方法:
每当试图通过类型为 AliasedType 的 glvalue 读取或修改类型为 DynamicType 的对象的存储值时,除非以下情况之一成立,否则行为是未定义的:
AliasedType 和 DynamicType 相似。
AliasedType 是 DynamicType 的(可能带 cv 限定符的)signed 或 unsigned 变体。
AliasedType 是 std::byte、char 或 unsigned char:这允许将任何对象的对象表示作为字节数组进行检查。

4

[expr.reinterpret.cast]/7:

一个对象指针可以显式转换为不同类型的对象指针。

[basic.compound]/3:

指向 cv void 或对象类型的指针类型称为 对象指针类型

不过,您不需要使用 reinterpret_cast。每个指向非 cv 限定的指向类型的对象指针类型都可以隐式转换为 void*,而反向转换可以使用 static_cast


感谢您提供如此详细的答案...不幸的是,这与我的误解有些不同。正如您从问题的最后一行所看到的那样,我正在尝试将int **转换为void *,我相信这确实需要一个reinterpret_cast - Jonathan Mee
不是吗?“指向int *的指针”就是指向CV未限定对象类型的指针。 - cpplearner
取消那个,是从void*int**的转换需要一个reinterpret_cast。你同意这个吗?我盗用了这个答案中的例子来演示:https://ideone.com/G4zuwd 除非你知道我不知道的东西,否则我需要一个reinterpret_cast来分配ptr,对吧? - Jonathan Mee
“从void*到int**的转换需要reinterpret_cast”是错误的。你只需要一个单一的static_cast:https://godbolt.org/z/Khvn145G5 - Mike Kaganski

2

将指向一种类型的指针转换为指向不同类型(包括void)的指针始终是合法的,因此如果T是一种类型,则以下C++代码是合法的:

最初的回答:在C++中,将指向一种类型的指针转换为指向另一种类型(包括void)的指针是合法的。因此,如果T是一种类型,下面的代码是合法的:

T* x;
void *y = reinterpret_cast<void *>(x);

在现实世界中,它从未被使用,因为void *是一个特殊情况,您可以使用static_cast获得相同的值:

最初的回答:

void *y = static_cast<void *>(x); // equivalent to previous reinterpret_cast

最初的回答已经隐式地转换,可以简单地写成void *y = x; - 感谢Michael Kenzel注意到它。
更明确地说,标准甚至在C++17草案n4659的8.2.10 Reinterpret cast [expr.reinterpret.cast] §7中提到:

当将对象指针类型的prvalue v转换为对象指针类型“指向cv T”的结果是static_cast<cv T*>(static_cast<cv void*>(v))

当您提到字节和字符是唯一合法类型时,只是因为仅对这些类型的转换后的指针进行解引用才是合法的。这里不包括void,因为您永远无法解引用void *
具体回答您的问题:

..我正在从int **转换为void*。最终我将从void*转换回int**。

标准保证第一个是标准(读作隐式)转换:

对象类型T的“指向cv T”的指针类型的prvalue可以转换为类型“指向cv void”的prvalue。指针值(6.9.2)由此转换不变。

所以这总是合法的:
int **i = ...;
void *v = i;

对于反向转换,标准规定(在static_cast段落中):

可以将类型为“指向cv1 void”的prvalue转换为类型为“指向cv2 T”的prvalue,

因此这也是合法的。

“prvalue”是C++中的一个概念,表示纯右值(pure rvalue),即不可修改的临时对象。cv1和cv2分别代表两个类型的常量性和易变性。

int **j = static_cast<int **>(v);

最初的回答: 标准确保了j == i
这个标准保证了变量j和i是相等的。

你的例子中甚至不需要使用 static_cast。任何对象指针类型都可以隐式转换为 void*(具有相同的 cv 限定符)。只有在将 void* 强制转换回特定类型的对象指针时才需要使用 static_cast... - Michael Kenzel
感谢您提供如此详细的答案...不幸的是,这与我的误解有些不同。正如您可以从问题的最后一行看到的那样,我正在尝试将int **转换为void *,然后再转回来,我相信这确实需要使用reinterpret_cast - Jonathan Mee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接