为什么使用static_cast <int>(x)代替(int)x?


667

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


36
提出异议,问并回答
Graeme Perrow

25
我不同意,这另一个问题是关于描述C ++中引入的强制类型转换之间的差异。这个问题是关于static_cast的实际用途的,这有点不同。
文森特·罗伯特

2
我们当然可以合并两个问题,但是我们需要从该线程中保留的是使用函数而不是C样式强制转换的优点,C强制类型转换目前仅在另一线程的单行答案中提到,没有投票。
Tommy Herbert

6
该问题与int等“内置”类型有关,而该问题与类类型有关。这似乎是一个足够大的差异,值得单独解释。

9
static_cast实际上是运算符,而不是函数。
ThomasMcLeod

Answers:


633

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

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

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风格的转换更加危险,而且要确保它们正确无误地查找它们要困难得多。


30
您不应该static_cast用于强制继承层次结构,而应使用dynamic_cast。这将返回空指针或有效指针。
David Thornley 2010年

41
@David Thornley:我通常会同意。我想我已经指出了static_cast在这种情况下使用的注意事项。dynamic_cast也许更安全,但这并不总是最好的选择。有时您确实知道,通过编译器不透明的方式,指针指向给定的子类型,并且a static_cast更快。至少在某些环境中,dynamic_cast需要可选的编译器支持和运行时成本(启用RTTI),并且您可能不希望仅为执行两项检查就启用它。C ++的RTTI只是该问题的一种可能解决方案。
Euro Micelli 2010年

18
您关于C强制转换的说法是错误的。所有的C转换都是值转换,大致可以与C ++相比static_cast。与C等效的reinterpret_cast*(destination_type *)&,即获取对象的地址,将该地址转换为指向其他类型的指针,然后取消引用。除字符类型或某些结构类型的情况下,其为C定义这个构建体的行为,它通常导致在C.未定义的行为
R.,GitHub的停止帮助ICE

11
您的好答案可以解决该帖子的正文。我一直在寻找标题“为什么要使用static_cast <int>(x)而不是(int)x”的答案。也就是说,对于类型int(和int单独使用),为什么将static_cast<int>vs. (int)用作唯一的好处似乎在于类变量和指针。要求您对此进行详细说明。
chux-恢复莫妮卡

31
@chux,因为int dynamic_cast不适用,但是所有其他原因仍然有效。例如:假设v一个函数参数声明为float(int)v则为static_cast<int>(v)。但是,如果你改变参数float*(int)v悄悄地成为reinterpret_cast<int>(v)虽然static_cast<int>(v)是非法的,由编译器正确捕获。
Euro Micelli 2013年

115

一个实用的技巧:如果您打算整理项目,则可以在源代码中轻松搜索static_cast关键字。


3
您也可以使用方括号(例如“(int)”)进行搜索,但这是使用C ++样式转换的良好答案和有效理由。
2014年

4
@Mike将发现误报-具有单个int参数的函数声明。
内森·奥斯曼

1
这可能会带来误报:如果您在不是唯一作者的代码库中搜索,由于某些原因,其他人可能会引入C风格的强制类型转换。
Ruslan

7
这样做如何帮助整理项目?
Bilow

您不会搜索static_cast,因为它很可能是正确的。在搜索reinterpret_cast,const_cast甚至dynamic_cast时,您想过滤掉static_cast,因为它们将表明可以重新设计的位置。C-cast混合在一起,没有给出铸造的原因。
德拉甘

78

简而言之

  1. static_cast<>() 为您提供了编译时检查功能,而C样式强制转换则没有。
  2. static_cast<>()可以很容易地在C ++源代码中的任何位置发现它;相反,C_Style强制转换很难发现。
  3. 使用C ++强制转换可以更好地传达意图。

更多说明

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

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

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

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

与C样式强制转换相反,静态强制转换将允许编译器检查指针和指针数据类型是否兼容,从而允许程序员在编译期间捕获此错误的指针分配。

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

阅读更多内容:
static_cast <>和C样式转换
常规转换与static_cast与dynamic_cast有什么区别?


21
我不同意这样的说法static_cast<>()。我的意思是,有时候是这样,但是在大多数情况下(尤其是在基本整数类型上),它只是非常可怕且不必要地冗长。例如:此函数交换32位字的字节。使用static_cast<uint##>()强制转换几乎是不可能阅读的,但是使用强制转换很容易理解(uint##)代码图片: imgur.com/NoHbGve
Todd Lehman,

3
@ToddLehman:谢谢,但我也没有说always。(但是大多数时候是)在某些情况下,c样式强制转换的可读性更高。这就是c样式转换仍然有效并在c ++ imho中发挥作用的原因之一。:)顺便说一句,这是一个很好的例子
里卡(Rika)

8
该图像中的@ToddLehman代码使用链接的两个强制转换((uint32_t)(uint8_t))来实现除最低字节以外的字节被重置。为此,按位和(0xFF &)。强制转换的用法混淆了意图。
OO Tiib

28

这个问题比仅使用凋零的static_cast或C样式转换要大,因为使用C样式转换时会发生不同的事情。C ++强制转换运算符旨在使这些操作更明确。

从表面上看,static_cast和C样式强制转换看起来是一样的,例如,将一个值强制转换为另一个值时:

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的实例。但是,如果您当时不知道代码实际指向的内容,该怎么办?(2)也许是完全合法的(您只想查看整数的一个字节),但也可能是一个错误,在这种情况下,一个错误会很好,例如(3)。C ++强制转换运算符旨在通过在可能时提供编译时或运行时错误来在代码中暴露这些问题。

因此,对于严格的“价值转换”,可以使用static_cast。如果要在运行时对指针进行多态转换,请使用dynamic_cast。如果您真的想忘记类型,可以使用reintrepret_cast。要将const抛出窗口,可以使用const_cast。

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


26

static_cast表示您不能偶然const_castreinterpret_cast,这是一件好事。


4
与C样式强制转换相比,其他优点(尽管相当次要)是,它更加突出(做一些可能不好的事情看起来很丑),并且更具可重复性。
Michael Burr

4
在我的书中,grep-ability总是一个加号。
Branan

7
  1. 允许使用grep或类似工具在您的代码中轻松找到强制类型转换。
  2. 明确说明您正在执行哪种类型的强制转换,并请编译器帮助实施。如果只想放弃常量性,则可以使用const_cast,它将不允许您进行其他类型的转换。
  3. 强制转换本质上很丑陋-作为程序员,您正在否定编译器通常如何对待您的代码。您对编译器说:“我比您了解得更多。” 在这种情况下,执行强制转换应该是一件很痛苦的事情,并且应该将它们坚持在您的代码中是有意义的,因为它们可能是问题的根源。

请参阅有效的C ++简介


对于类,我完全同意,但是对POD类型使用C ++样式转换是否有意义?
2014年

我认同。所有这三个原因都适用于POD,并且只使用一个规则而不是将类和POD分开是有帮助的。
JohnMcG 2014年

有趣的是,我可能必须在以后的POD类型代码中修改我的类型转换。
Zachary Kraus 2014年

7

这是关于您要强加多少类型安全性。

当你写(bar) foo(相当于reinterpret_cast<bar> foo没有提供类型转换运算符),您是在告诉编译器忽略类型安全性,并按照提示执行操作。

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


编辑2014-02-26

我在5年前就写了这个答案,但我弄错了。(请参阅评论。)但是它仍然会被投票!


8
(bar)foo不等同于reinterpret_cast <bar>(foo)。“(TYPE)expr”的规则是它将选择要使用的适当C ++样式强制转换,其中可能包括reinterpret_cast。
理查德·科登

好点子。欧米塞利(Euro Micelli)对这个问题给出了明确的答案。
皮塔鲁

1
另外,它是static_cast<bar>(foo),并带有括号。相同reinterpret_cast<bar>(foo)
LF

6

C样式强制转换很容易在代码块中丢失。C ++样式转换不仅是更好的做法,它们提供了更大的灵活性。

reinterpret_cast允许整数到指针类型的转换,但是如果使用不当,可能是不安全的。

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

另一方面,dynamic_cast将执行这些检查,标记所有不明确的分配或转换。它仅适用于指针和引用,并且会产生开销。

还有其他一些,但是这些是您会遇到的主要问题。


4

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

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

4
static_cast<int>(d)但是,为什么(int)d这么简明易懂,却有人写作呢?(我的意思是基本类型,而不是对象指针。)
Todd Lehman

@ gd1 —为什么有人会把一致性放在可读性之上?(实际上是严重的一半)
托德·雷曼

2
@ToddLehman:我,考虑到仅仅因为某些类型对您特殊而对它们进行例外处理对我来说没有任何意义,而且我也不同意您的可读性概念。从我在另一条评论中发布的图像可以看出,更短并不意味着更具可读性。
gd1

2
static_cast是做出非常特殊的转换的明确而有意识的决定。因此,它增加了意图的清晰度。在代码审查,错误或升级练习中,它也很方便用作标记来搜索源文件以进行转换。
Persixty

1
@ToddLehman观点:为什么人们(int)dint{d}可读性更高的时候写东西?构造函数或类似于函数的函数(如果有的话())的语法并没有那么快,无法演变为复杂表达式中令人费解的圆括号。在这种情况下,它将是int i{d}而不是int i = (int)d。更好的IMO。就是说,当我只需要一个表达式中的临时变量时,我会使用static_cast并且从未使用过构造函数强制类型转换,我不认为。我只(C)casts在急着编写debug couts 时使用...
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.