c++ 在将void* 强制转换为任何类型时,我应该使用static_cast还是reinterpret_cast

oogrdqng  于 2022-12-24  发布在  其他
关注(0)|答案(9)|浏览(319)

static_castreinterpret_cast似乎都能很好地将void*转换为另一个指针类型。是否有一个好的理由来支持其中一个?

35g0bw71

35g0bw711#

使用static_cast:它是精确描述这里所进行的转换的最窄的类型转换。

有一种误解认为使用reinterpret_cast会更好,因为它意味着“完全忽略类型安全,只从A强制转换到B”。
然而,这实际上并没有描述reinterpret_cast的效果,相反,reinterpret_cast有很多含义,所有这些含义都表明“reinterpret_cast执行的Map是由实现定义的”。5.2.10.3
但是在从void*转换到T*的特定情况下,Map完全由标准良好定义;也就是说,在不改变指针地址的情况下给无类型指针赋值。
这是一个更喜欢static_cast的原因。
此外,更重要的是,每次使用reinterpret_cast都是非常危险的,因为它会将任何东西转换为其他任何东西(对于指针),而static_cast的限制性更强,因此提供了更好的保护级别。这已经使我避免了错误,我不小心试图将一种指针类型强制转换为另一种。

owfi6suc

owfi6suc2#

static_cast更适合于将void*转换为其他类型的指针。
当两个类型之间存在自然、直观的转换,但不一定保证在运行时工作时,static_cast是首选的强制转换。例如,可以使用static_cast将基类指针转换为派生类指针,这种转换在某些情况下有意义,但只有在运行时才能验证。类似地,您可以使用static_castint转换为char,这是定义良好的,但在执行时可能会导致精度损失。
另一方面,reinterpret_cast是一个造型运算符,用于执行根本不安全或不可移植的转换。例如,可以使用reinterpret_castvoid *转换为int。如果您的系统恰好有sizeof (void*)sizeof (int),它将正常工作。您也可以使用reinterpret_castfloat*转换为int*,反之亦然。其是平台特定的,因为floatint的特定表示不保证彼此具有任何共同之处。
简而言之,如果您发现自己在执行转换时,强制转换在逻辑上有意义,但在运行时不一定成功,请避免使用reinterpret_cast。如果您事先知道强制转换在运行时可以工作,并通知编译器“我知道这可能不起作用,但至少它是有意义的并且我有理由相信它将在运行时正确地做正确的事情。”编译器然后可以检查强制转换是在相关类型之间,报告编译时错误。使用reinterpret_cast对指针转换执行此操作完全绕过编译时安全检查。
在一些情况下,您可能希望使用dynamic_cast而不是static_cast,但这些情况大多涉及类层次结构中的强制转换,并且(很少)直接涉及void*
至于规范更喜欢哪一个,没有一个被过多地提到是“最适合使用的”(或者至少,我不记得有一个是这样提到的)。但是,我认为规范希望您使用static_cast而不是reinterpret_cast

A* ptr = (A*) myVoidPointer;

尝试的强制转换操作符的顺序总是尝试在使用reinterpret_cast之前使用static_cast,这是您想要的行为,因为reinterpret_cast不保证是可移植的。

svmlkihl

svmlkihl3#

这是一个坚韧的问题。一方面,Konrad对reinterpret_cast的规范定义提出了一个很好的观点,尽管实际上它可能做同样的事情。另一方面,如果你在指针类型之间进行强制转换(例如,当通过char* 在存储器中索引时,这是相当常见的),static_cast将生成编译器错误,并且无论如何您将被迫使用reinterpret_cast
在实践中我使用reinterpret_cast,因为它更能描述类型转换操作的意图,你当然可以使用不同的操作符来指定指针重新解释(这保证了返回相同的地址),但是标准中没有。

iibxawm4

iibxawm44#

您可能通过隐式转换获得了void*,因此应该使用static_cast,因为它最接近隐式转换。

pdkcd3nj

pdkcd3nj5#

使用static_cast和使用reinterpret_castvoid*之间转换是相同的。请参见the link中的答案。但通常首选static_cast,因为它更窄,并且通常(但不是在此特定情况下)转换更安全。

klsxnrf1

klsxnrf16#

关于实现定义的Map存在混淆。这是关于 * mapping 。实现可以内部Map它喜欢的,但是它必须做出其他保证。reinterpret_cast的结果不能简单地任意指向实现会认为其他对象的位置--尽管 outward representation 可能不同。(虽然转换为整数和返回整数将具有原始值,在特定情况下,概述)。基本上,实现的重新解释的强制转换是否返回相同的“内存位置”是无关紧要的;无论它返回什么,都被Map到相同的“值”(顺便说一句,核心准则明确回答了使用reinterpret_cast(char/unsigned char*/std::byte*)查看原始对象表示是定义行为的情况)。
相关标准规定void* cast:

静态类型转换

类型“cv1 void指针”的纯右值可转换为类型“cv 2 T指针”的纯右值,其中T是对象类型,cv 2的cv限定与cv1相同或大于cv1。如果原始指针值表示内存中字节的地址A,并且A不满足T的对齐要求,则未指定结果指针值。否则,如果原始指针值指向一个对象a,并且有一个T类型的对象b(忽略cv限定),该对象b与a是指针可互换的(6.8.3),则结果是指向b的指针。否则,指针值不会因转换而改变。[例3:T* p1 = new T; const T* p2 = static_cast<const T*>(static_cast<void*>(p1)); bool b = p1 == p2; // b will have the value true.-结束示例]

重新处理转换

对象指针可以显式转换为不同类型的对象指针。当对象指针类型的纯右值v转换为对象指针类型“指针指向cv T”时,结果为**static_cast<cv T*>(static_cast<cv void*>(v))**
关键是最后一句,为了这个问题的void* 转换,(假设对象类型满足对齐要求、cv限定,并且是安全派生的指针):
reinterpret_cast T* from void*等于static_cast T* from void*
但是你绝对应该,绝对绝对绝对绝对使用static_cast,没有其他原因,关于重新解释铸造和ISO标准的复杂性的可怕的民间传说可能会导致你不必要地被同行长篇大论。😃
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Ru-pun

ukdjmx9f

ukdjmx9f7#

为此使用static_cast。只有在没有其他方法的极少数情况下才使用reinterpret_cast

vxf3dgd4

vxf3dgd48#

我建议总是使用最弱的演员阵容。
reinterpret_cast可用于强制转换指向float的指针。强制转换的结构破坏越多,使用时就越需要注意。
char*的情况下,我会使用c风格的造型,直到我们有一些reinterpret_pointer_cast,因为它的弱,没有其他足够的。

v8wbuo2f

v8wbuo2f9#

reinterpret_cast将强制将void*转换为目标数据类型。它不保证任何安全性,并且您的程序可能会崩溃,因为底层对象可能是任何对象。
例如,您可以将myclass*类型转换为void*,然后使用reinterpret_cast将其转换为可能具有完全不同布局的yourclass*
因此,建议使用static_cast

相关问题