使用static_cast而不是C风格强制类型转换对非指针类型有什么优势吗?

35

我很清楚使用static_cast而非C风格的指针类型转换的优势。

如果指针类型不兼容,则:

  • static_cast将在源代码中的特定行产生编译时错误
  • C风格转换可能会在程序执行的“随机”点导致运行时错误

但是我找不到任何关于非指针类型的类似示例。

换句话说,对于非指针类型,两种转换方法产生相同的结果。

这个理解正确吗?或者我有遗漏的地方吗?

如果是,static_cast是否仅用于非指针类型以维护编码一致性?


5
C语言风格的强制类型转换并不关心输入或输出是否是指针。它可以愉快地在指针和非指针之间进行转换。 - Ulrich Eckhardt
2
@Dinal24:这并不涉及到我具体问题的优点,即在非指针类型的情况下使用static_cast而不是常规转换。 - barak manos
3
Scott Meyers 在他极棒的《Effective C++》书中的第二条建议中,有一个专门标注为“优先使用 C++ 式转型”的部分,涉及到了这个确切的主题。 - Frerich Raabe
1
我认为这不是这个“规范”问题的重复。这个问题的答案可以从常见问题的答案中推断出来,但在常见问题中没有明确说明。 - Angew is no longer proud of SO
3
这里给出的答案与其他重复问题的答案完全相同,除了明确的“是”。尽管this answer明确解释了为什么你会更喜欢在所有情况下使用C++风格的转换... - rubenvb
显示剩余4条评论
4个回答

33

其他两个回答中尚未提到的一个优点是,static_cast 很容易被发现。在 C++ 中,括号的含义通常非常复杂,很难发现恶意(甚至不正确)的强制转换。但当我看到以 _cast 结尾的东西时,它就像是一个精神减速带:我会放慢脚步并仔细检查为什么要破坏类型系统。


11
易读性有时比技术原因更重要。 - Michael Gazonda
4
易读性和易于识别是不错的,但 static_cast 相对于 C 风格转换的真正优势在于使用 CTRL+F 查找时表现得更好。 你曾经尝试过在代码库的主要部分中查找所有现有的 C 风格转换吗? - Kaiserludi
@Kaiserludi 我想“工具可读性”也可以被认为是“可读性”。 - Frerich Raabe
这并不像看起来那样简单明了。引入C++风格的转换的整个要点是将各种责任分配到不同类型的转换中,而不是将它们全部堆积在C风格的转换上。然而,即使进行了这种分配,static_cast 仍然似乎做了太多的事情。 它涵盖了两个完全不相关的领域:非平凡指针转换(层次向上转换和从 void * 转换)以及算术转换。 通过 static_cast 进行所有这些实际上使它们更难以发现。 - AnT stands with Russia
我希望前者和后者之间有明显的视觉区别。我希望arithmetic_cast作为一种单独的转换方式。但是,我希望它有一个比完整的arithmetic_cast更紧凑的名称(那将会很笨重)。这就是C风格转换非常适合的地方。C风格转换用于算术转换,static_cast用于指针转换。你说它们更难以发现?我不记得有任何需要故意发现算术转换的需求。指针转换-是的,但不是算术转换。算术转换是自然的。它们不应该比例如+运算符更容易“发现”。 - AnT stands with Russia

17

我假设避免使用指针的简单引用并不计入。

在这种情况下:C风格的转换可以是:

  • const_cast
  • static_cast
  • static_cast 后跟 const_cast,
  • reinterpret_cast
  • reinterpret_cast 后跟 const_cast

需要注意的是:static_cast 对于不可访问的基类的限制被解除了。

const_cast 仅适用于指针和引用。

reinterpret_cast 仅适用于指针和引用。它包括指针与整数之间的转换,但仍需涉及指针类型。

对于static_cast 的特殊例外情况,仅适用于指针和引用。

所以,是的,通过排除指针和引用,您已经排除了 C风格转换在 static_cast 上的所有支持功能。

如果是这样的话,static_cast 是否只用于非指针类型,以保持编码一致性?

让我们来举一个类比:我不会用链锯来打开一袋薯片。虽然我可以这样做,但链锯很危险,因此使用它会引入不必要的风险。使用链锯很容易出错,如果我使用不当,就没有安全机制可以防止事故发生。


2
你的比喻可以更生动一些:你当然可以用链锯剪脚趾甲,但最好知道自己在做什么。 - Frerich Raabe
1
@FrerichRaabe 另一方面,如果你知道自己在做什么,就不会用链锯剪脚趾甲。 :) - user743382
3
恰当的隐喻是,你应该非常确定你要用火焰喷射器打开的是装有薯片的袋子而不是爆米花或炸药的袋子,因为它们会以不同程度对你造成破坏。 - Peter - Reinstate Monica
@PeterSchneider 嘿,听起来不错。这也适用于链锯:最好不要使用链锯代替打开玻璃罐盖子的方法。 - user743382

16

仅用于非指针类型以保持编码一致性吗?

不仅如此,还有助于维护正确性和未来兼容性。

它有助于维护正确性,因为 static_cast 无法执行某些C样式转换。如果涉及的类型有误,这将有所帮助,例如在处理指针时却认为它是整数(在多层模板和typedefs下,这并不难想象)。因此,如果你打算进行 static_cast ,写一个并获得编译时错误,你会立即被告知所涉及的类型与你的想法不符。而使用C样式转换的“不限制任何内容”的态度,则会让你没有时间发现这个错误。

它还有助于未来的兼容性。如果转换中涉及的类型在开发后更改, static_cast 将报告错误,而C样式转换则只会默默地更改其行为(可能不是预期的行为)。


使用C风格的强制类型转换,我将无法及时发现错误 - 这只适用于指针之间的转换吗?你能给出一个非指针的例子来支持这个说法吗? - barak manos
@barakmanos 我的意思是你百分之百确信你在转换整数,但其中一个实际上是指针。我进行了编辑以澄清。 - Angew is no longer proud of SO
1
换句话说,使用 static_cast 可以帮助您强制执行这个理想的约束条件,即源类型不是指针。int *ip = new int(); long j = static_cast<long>(*ip); 是一个愚蠢的例子,但像这样编写可以捕捉到将 ip 打成 *ip 的错误,而 long(*ip) 无法捕捉到该错误,因为 long(ip) 是一个有效的 C 风格转换。 - Steve Jessop

6
“static_cast”仅用于非指针类型以保持编码一致性吗?
不是(好吧-是的,但不仅如此)。它也易于查找/替换。这在重构、修复错误等情况下很重要。
这是对所允许的强制类型转换的限制:考虑一个示例,其中您对变量进行了C样式转换(可以正常工作),然后将转换后的变量声明更改为某些使转换无效的内容(例如,将“int x;”更改为“void * const x;”)。
C样式转换突然执行另一个函数(即使用相同代码的“const_cast <...>(reinterpret_cast <...>(...)”)。
如果您最初使用static_cast而不是C样式转换编写代码,则编译器会告诉您,其中的static_cast实际上并未执行静态转换。

是的,在我提出这个问题几个小时后,我想到了查找/替换。谢谢。 - barak manos

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