CppCoreGuidelines ES.48 避免类型转换
23 April 2023
“Avoid casts”
理由
类型转换容易导致错误、导致代码优化不可靠。
坏例子
double d = 2; auto p = (long*)&d; auto q = (long long*)&d; cout << d << ' ' << *p << ' ' << *q << '\n';
2 0 4611686018427387904
不同实现,以上代码的输出也不一样。
如果中间修改,结果更加奇怪:
double d = 2; auto p = (long*)&d; auto q = (long long*)&d; *q = 666; cout << d << ' ' << *p << ' ' << *q << '\n';
3.29048e-321 666 666
很奇怪吧,好在还没有导致程序崩溃。
注意
程序员用上类型转换,往往都觉得这是自己想要做的,觉得这样写代码更容易读。事实上,他们违背了使用数值的通用规则。重载解析或者模板实例化往往能够帮你选择合适的函数。如果没有重载或模板实例化,可能需要去实现这些,而非进行局部修理(类型转换)。
注意
对于系统开发语言来说,类型转换也是必不可少的。比如,除了类型转换,还有什么把设备寄存器的地址存入指针?但是,总的来说,类型转换的使用还是严重过度了。是代码错误很主要的来源。
如果你感觉需要很多类型转换,那么,你肯定遇到很根本的设计问题。
类型规则集禁止使用reinterpret_cast
和 C 风格的类型转换。
不要把[[nodiscard]]
返回值强制转换成 void
。如果你确实有意要抹掉返回值?如果代码评审和你都觉得忽略返回值是需要的。那么可以使用std::ignore =
来消除编译器警告。这个方式更简单、可移植,也更容易通过 grep 搜索到。
[[nodiscard]] int get_value() { return 42; } int main() { std::ignore = get_value(); // no warning now: warning: ignoring // return value of 'int get_value()', // declared with attribute 'nodiscard' // [-Wunused-result] return EXIT_SUCCESS; }
强制类型转换用的优点过度。现代C++提供规则和机制避免在大部分情况下使用强制类型转换。比如:
- 使用模板
- 使用
std::variant
- 使用定义良好的、安全、隐式指针类型转换
- 使用
std::ignore =
忽略[[nodiscard]]
返回值
强化
- 标记 C 风格的强制类型转换,包括转换成
void
的情况 - 标记函数形式的强制类型转换
Type(value)
。使用Type{value}
,这个方式可以避免变窄转换。 - 标记源和目标类型一样的指针类型转换
- 标记可以隐式转换的显示的指针类型转换