为什么使用 static_cast<T>(x) 而不是 (T)x?

Why use static_cast<T>(x) instead of (T)x?

提问人:Tommy Herbert 提问时间:9/20/2008 最后编辑:Jan SchultkeTommy Herbert 更新时间:10/7/2023 访问量:429013

问:

我听说函数应该比 C 样式或简单的函数样式转换更可取。这是真的吗?为什么?static_cast

C++ 强制转换 static-cast

评论

43赞 Graeme Perrow 9/20/2008
反对你的荣誉,问和回答
31赞 Vincent Robert 9/20/2008
我不同意,另一个问题是关于描述 C++ 中引入的强制转换之间的差异。这个问题是关于static_cast的真正用处,略有不同。
2赞 Tommy Herbert 9/20/2008
我们当然可以合并这两个问题,但我们需要从这个线程中保留的是使用函数而不是 C 样式转换的优势,目前在另一个线程中只在一行答案中提到,没有投票。
6赞 4/20/2011
这个问题是关于“内置”类型的,比如 int,而这个问题是关于类类型的。这似乎是一个足够大的差异,值得单独解释。
13赞 ThomasMcLeod 2/25/2013
static_cast实际上是一个运算符,而不是一个函数。

答:

5赞 prakash 9/20/2008 #1

static_cast,除了操作指向类的指针之外,还可用于执行类中显式定义的转换,以及执行基本类型之间的标准转换:

double d = 3.14159265;
int    i = static_cast<int>(d);

评论

7赞 Todd Lehman 8/5/2015
但是,为什么有人会写,什么时候更简洁,可读性更强?(我的意思是在基本类型的情况下,而不是对象指针。static_cast<int>(d)(int)d
0赞 Todd Lehman 1/14/2016
@gd1 — 为什么有人会把一致性放在可读性之上?(其实是半认真的)
2赞 gd1 1/14/2016
@ToddLehman : 我,考虑到仅仅因为它们对你来说很特别而对某些类型进行例外对我来说没有任何意义,而且我也不同意你的可读性概念。更短并不意味着更具可读性,正如我从您在另一条评论中发布的图片中看到的那样。
2赞 Persixty 1/18/2016
static_cast是一个明确而有意识的决定,要做出一种非常特殊的转变。因此,它增加了意图的明确性。作为标记,在代码审查、错误或升级练习中搜索源文件以查找转换也非常方便。
1赞 underscore_d 5/13/2017
@ToddLehman对立面:为什么有人会在可读性如此之高的时候写作?构造函数,或者类似函数(如果有的话)语法不会那么快,以至于在复杂的表达式中变成一个噩梦般的括号迷宫。在本例中,它将代替 .更好的IMO。也就是说,当我只需要在表达式中临时使用时,我使用并且从未使用过构造函数强制转换,我不认为。我只在匆忙写调试时使用...(int)dint{d}()int i{d}int i = (int)dstatic_cast(C)castscout
28赞 DrPizza 9/20/2008 #2

static_cast意味着你不能偶然或,这是一件好事。const_castreinterpret_cast

评论

4赞 Michael Burr 9/20/2008
与 C 风格演员相比,其他(尽管相当小)的优势是它更突出(做一些潜在的坏事应该看起来很丑),而且它更易于理解。
4赞 Branan 9/23/2008
在我的书中,grep-ability 始终是一个加分项。
6赞 Pitarou 9/20/2008 #3

这是关于你想施加多少类型安全。

当你编写时(这相当于你没有提供类型转换运算符),你是在告诉编译器忽略类型安全,而只是按照它的指示去做。(bar) fooreinterpret_cast<bar> foo

当你编写时,你要求编译器至少检查类型转换是否有意义,对于整型类型,插入一些转换代码。static_cast<bar> foo


编辑 2014-02-26

我在 5 年多前写了这个答案,但我弄错了。(请参阅注释。但它仍然会得到点赞!

评论

9赞 Richard Corden 9/20/2008
(bar)foo 不等同于 reinterpret_cast<bar>(foo)。“(TYPE) expr”的规则是,它将选择适当的C++样式转换来使用,其中可能包括reinterpret_cast。
1赞 Pitarou 9/21/2008
好点子。Euro Micelli 对这个问题给出了明确的答案。
1赞 L. F. 7/25/2019
此外,它是 ,带有括号。相同。static_cast<bar>(foo)reinterpret_cast<bar>(foo)
128赞 Karl 9/20/2008 #4

一个实用的提示:如果你打算整理项目,你可以轻松地在源代码中搜索关键词。static_cast

评论

7赞 Mike 1/7/2014
您也可以使用括号进行搜索,例如“(int)”,但很好的答案和使用 C++ 样式转换的正当理由。
8赞 Nathan Osman 9/13/2016
@Mike会发现误报 - 具有单个参数的函数声明。int
1赞 Ruslan 6/9/2017
这可能会产生假阴性结果:如果你正在搜索一个你不是唯一作者的代码库,你不会找到其他人可能出于某些原因引入的 C 风格转换。
16赞 Bilow 10/11/2017
这样做对整理项目有什么帮助?
0赞 Dragan 5/18/2020
您不会搜索static_cast,因为它很可能是正确的。在搜索reinterpret_cast、const_cast甚至dynamic_cast时,您希望过滤掉static_cast,因为这些会指示可以重新设计的地方。C-cast 将所有内容混合在一起,不会给你铸造的理由。
741赞 Euro Micelli 9/20/2008 #5

主要原因是经典的 C 强制转换没有区分我们所说的 、、 和 。这四件事是完全不同的。static_cast<>()reinterpret_cast<>()const_cast<>()dynamic_cast<>()

A 通常是安全的。语言中存在有效的转换,或者适当的构造函数使之成为可能。唯一有点冒险的时候是当你扔到一个继承的类时;您必须确保该对象实际上是您声称的后代,通过语言外部的方式(如对象中的标志)。只要检查结果(指针)或考虑可能的异常(引用),A 就是安全的。static_cast<>()dynamic_cast<>()

另一方面,A(或A)总是危险的。你告诉编译器:“相信我:我知道这看起来不像一个(这看起来好像它不可变),但它是”。reinterpret_cast<>()const_cast<>()foo

第一个问题是,如果不查看大型和分散的代码片段并了解所有规则,几乎不可能判断哪一个会在 C 风格的强制转换中出现。

让我们假设这些:

class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;

CMyBase  *pSomething; // filled somewhere

现在,这两者的编译方式相同:

CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked

pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>
                                     // Safe; as long as we checked
                                     // but harder to read

但是,让我们看看这个几乎相同的代码:

CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert

pOther = (CMyOtherStuff*)(pSomething);            // No compiler error.
                                                  // Same as reinterpret_cast<>
                                                  // and it's wrong!!!

正如你所看到的,如果不了解所涉及的所有类,就没有简单的方法来区分这两种情况。

第二个问题是 C 型演员表太难定位了。在复杂的表达式中,很难看到 C 样式的强制转换。如果没有成熟的 C++ 编译器前端,编写需要定位 C 样式转换的自动化工具(例如搜索工具)几乎是不可能的。另一方面,搜索“static_cast<”或“reinterpret_cast<”很容易。

pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
      // No compiler error.
      // but the presence of a reinterpret_cast<> is 
      // like a Siren with Red Flashing Lights in your code.
      // The mere typing of it should cause you to feel VERY uncomfortable.

这意味着,不仅 C 型演员更危险,而且要找到它们以确保它们正确要困难得多。

评论

37赞 David Thornley 1/21/2010
不应用于向下转换继承层次结构,而应使用 .这将返回 null 指针或有效指针。static_castdynamic_cast
50赞 Euro Micelli 1/21/2010
@David Thornley:我通常同意。我想我指出了在这种情况下使用的注意事项。 可能更安全,但这并不总是最好的选择。有时您确实知道指针指向给定的子类型,这意味着编译器不透明,并且 a 更快。至少在某些环境中,需要可选的编译器支持和运行时成本(启用 RTTI),并且您可能不希望仅为您可以自己执行的几个检查而启用它。C++ 的 RTTI 只是该问题的一种可能解决方案。static_castdynamic_caststatic_castdynamic_cast
23赞 R.. GitHub STOP HELPING ICE 2/4/2012
你关于 C 强制转换的说法是错误的。所有 C 转换都是值转换,大致可与 C++ 相媲美。C 等价物是 ,即获取对象的地址,将该地址转换为指向不同类型的指针,然后取消引用。除了字符类型或某些结构类型(C 定义此构造的行为)外,它通常会导致 C 中未定义的行为。static_castreinterpret_cast*(destination_type *)&
13赞 chux - Reinstate Monica 12/15/2013
您的精彩回答解决了帖子的正文。我一直在寻找标题“为什么使用 static_cast<int>(x) 而不是 (int)x”的答案。也就是说,对于类型(和单独),为什么使用与。 因为唯一的好处似乎是类变量和指针。请您详细说明这一点。intintstatic_cast<int>(int)
43赞 Euro Micelli 12/19/2013
@chux,因为不适用,但所有其他原因都成立。例如:假设是一个声明为 的函数参数,则为 。但是,如果将参数更改为 ,则悄悄地变成 while 是非法的,并且被编译器正确捕获。intdynamic_castvfloat(int)vstatic_cast<int>(v)float*(int)vreinterpret_cast<int>(v)static_cast<int>(v)
33赞 2 revs, 2 users 84%Dusty Campbell #6

这个问题比仅仅使用 Whether 或 C 型铸造更重要,因为使用 C 型铸造时会发生不同的事情。C++ 强制转换运算符旨在使这些不同的操作更加明确。static_cast<>

从表面上看,C样式的强制转换似乎是一回事,例如,当将一个值转换为另一个值时:static_cast<>

int i;
double d = (double)i;                  //C-style cast
double d2 = static_cast<double>( i );  //C++ cast

这两者都将整数值转换为双精度值。但是,当使用指针时,事情会变得更加复杂。一些例子:

class A {};
class B : public A {};

A* a = new B;
B* b = (B*)a;                                  //(1) what is this supposed to do?

char* c = (char*)new int( 5 );                 //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error

在此示例中,(1) 可能没问题,因为 A 指向的对象实际上是 B 的实例。但是,如果你在代码中不知道实际指向什么怎么办?a

(2) 可能是完全合法的(你只想看整数的一个字节),但它也可能是一个错误,在这种情况下,错误会很好,比如 (3)。

C++ 强制转换运算符旨在通过尽可能提供编译时或运行时错误来公开代码中的这些问题。

因此,对于严格的“价值转换”,您可以使用 .如果要对指针进行运行时多态转换,请使用 .如果你真的想忘记类型,你可以使用 .只是扔出窗外有.static_cast<>dynamic_cast<>reintrepret_cast<>constconst_cast<>

它们只是使代码更加明确,以便看起来您知道自己在做什么。

6赞 Konrad 9/20/2008 #7

C 样式的强制转换在代码块中很容易被遗漏。C++ 样式的强制转换不仅是更好的实践;它们提供了更大程度的灵活性。

reinterpret_cast允许对指针类型进行积分转换,但如果使用不当,则可能不安全。

static_cast为数值类型提供了良好的转换,例如从枚举到整数或整数到浮点数或您确信类型的任何数据类型。它不执行任何运行时检查。

另一方面,dynamic_cast将执行这些检查,标记任何模棱两可的分配或转换。它仅适用于指针和引用,并会产生开销。

还有其他几个,但这些是您将遇到的主要问题。

8赞 JohnMcG 9/20/2008 #8
  1. 允许在以下位置轻松找到石膏 使用 grep 或类似代码的代码 工具。
  2. 明确说明哪种 你正在做的演员,并且引人入胜 编译器帮助执行它。 如果你只想抛弃 const-ness,那么你可以使用 const_cast,这不会让你 进行其他类型的转换。
  3. 演员本质上是丑陋的——你作为 程序员正在推翻 编译器通常会处理你的 法典。你是在对 编译器,“我比你更清楚。 既然如此,这是有道理的 表演演员应该是 做中度痛苦的事情,以及 他们应该在你的 代码,因为它们是可能的来源 的问题。

请参阅有效的 C++ 简介

评论

0赞 Zachary Kraus 8/28/2014
我完全同意这一点,但是对 POD 类型使用 C++ 样式转换有什么意义吗?
0赞 JohnMcG 9/13/2014
我认为如此。所有 3 个原因都适用于 POD,并且只有一个规则会很有帮助,而不是为类和 POD 单独设置规则。
0赞 Zachary Kraus 9/14/2014
有趣的是,我可能不得不修改我在将来的 POD 类型代码中执行强制转换的方式。
119赞 Hossein 10/9/2014 #9

简而言之

  1. static_cast<>()给你一个编译时检查能力,C-Style 演员不会。
  2. static_cast<>()可以很容易地被发现 C++ 源代码中的任何位置;相比之下,C_Style演员更难被发现。
  3. 使用 C++ 强制转换可以更好地传达意图。

更多说明

静态强制转换在兼容类型之间执行转换。它 类似于 C 型演员表,但限制性更强。例如 C 样式强制转换将允许整数指针指向字符。

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

由于这会导致一个 4 字节指针指向分配的 1 个字节 内存,写入此指针将导致运行时错误或 将覆盖一些相邻的内存。

*p = 5; // run-time error: stack corruption

与 C 样式强制转换相比,静态强制转换将允许 编译器来检查指针和指针数据类型是否 compatible,这允许程序员捕捉到这个不正确的 编译期间的指针分配。

int *q = static_cast<int*>(&c); // compile-time error

阅读更多:
static_cast<> 和 C 风格铸造与常规铸造

static_cast 与 dynamic_cast 有什么区别

评论

27赞 Todd Lehman 8/5/2015
我不同意这更具可读性。我的意思是,有时是这样,但大多数时候——尤其是在基本的整数类型上——它只是可怕且不必要的冗长。例如:这是一个交换 32 位字字节的函数。使用演员表几乎不可能阅读,但使用演员表很容易理解。代码图片:imgur.com/NoHbGvestatic_cast<>()static_cast<uint##>()(uint##)
4赞 Hossein 5/23/2016
@ToddLehman:谢谢,但我也没说。(但大多数时候是的)当然,在某些情况下,c 样式的转换更具可读性。这就是 c 风格投射仍然活跃并在 c++ 恕我直言中发挥作用的原因之一。:)顺便说一句,这是一个很好的例子always
13赞 Öö Tiib 8/20/2018
该图像中的@ToddLehman代码使用两个链接的强制转换 () 来实现除最低字节之外的字节被重置。为此,有按位和 ()。强制转换的使用混淆了意图。(uint32_t)(uint8_t)0xFF &
1赞 John Doggett 12/28/2021
static_cast<int> 并非不可读,只是冗长。就编译时行为而言,它也是明确的。只有让我们相信,读者在模棱两可但可读的代码中发现的错误比编译器在编译语义上明确的代码时发现的错误要多,才能获胜。仅仅声称“这比更可读”是不能赢得胜利的——但如果确实是唯一的争论点,我认为static_cast<int>处于领先地位,特别是因为明确性本身就是可读性的一个属性。
2赞 j123b567 3/16/2022
@ToddLehman 您的代码是为什么首先要避免强制转换的确切示例。(请参阅不强制转换的替代实现 fxr.watson.org/fxr/source/lib/libkern/bswap32.c?v=NETBSD3)