Avoid casts
Reason
Casts are a well-known source of errors and make some optimizations unreliable.
Example, bad
double d = 2;
auto p = (long*)&d;
auto q = (long long*)&d;
cout << d << ' ' << *p << ' ' << *q << '\n';
What would you think this fragment prints? The result is at best implementation defined. I got
2 0 4611686018427387904
Adding
*q = 666;
cout << d << ' ' << *p << ' ' << *q << '\n';
I got
3.29048e-321 666 666
Surprised? It is actually undefined behavior, and so could also have crashed the program.
Note
Programmers who write casts typically assume that they know what they are doing, or that writing a cast makes the program "easier to read". In fact, they often disable the general rules for using values. Overload resolution and template instantiation usually pick the right function if there is a right function to pick. If there is not, maybe there ought to be, rather than applying a local fix (cast).
Notes
Casts are necessary in a systems programming language. For example, how else would we get the address of a device register into a pointer? However, casts are seriously overused as well as a major source of errors.
If you feel the need for a lot of casts, there might be a fundamental design problem.
The type profile bans reinterpret_cast and C-style casts.
Never cast to (void) to ignore a [[nodiscard]]return value. If you deliberately want to discard such a result, first think hard about whether that is really a good idea (there is usually a good reason the author of the function or of the return type used [[nodiscard]] in the first place). If you still think it's appropriate and your code reviewer agrees, use std::ignore = to turn off the warning which is simple, portable, and easy to grep.
Alternatives
Casts are widely (mis)used. Modern C++ has rules and constructs that eliminate the need for casts in many contexts, such as
- Use templates
- Use
std::variant - Rely on the well-defined, safe, implicit conversions between pointer types
- Use
std::ignore =to ignore[[nodiscard]]values.
Enforcement
- Flag all C-style casts, including to
void. - Flag functional style casts using
Type(value). UseType{value}instead which is not narrowing. (See ES.64.) - Flag identity casts between pointer types, where the source and target types are the same (#pro-type-identitycast).
- Flag an explicit pointer cast that could be implicit.