17

After reading here a lot of answers about C-style casting in C++ I still have one little question. Can I use C-style casting for built-in types like long x=(long)y; or it's still considered bad and dangerous?

ypnos
  • 50,202
  • 14
  • 95
  • 141
jackhab
  • 17,128
  • 37
  • 99
  • 136

9 Answers9

29

Can I use C-style casting for built-in types like long x=(long)y; or it's still considered bad and dangerous?

Don't use them, ever. The reasons against using them applies here as well. Basically, once you use them, all bets are off because the compiler won't help you any more. While this is more dangerous for pointers than for other types, it's potentially still dangerous and gives poor compiler diagnostics in the case of errors, whereas new style casts offer richer error messages since their usage is more constrained: Meyers cites the example of casting away constness: using any cast other than const_cast won't compile, thus making it clear what happens here.

Also, some other disadvantages apply regardless of the types, namely syntactic considerations: A C-style cast is very unobtrusive. This isn't good: C++ casts stand out clearly in the code and point to potentially dangerous code. They can also easily be searched for in IDEs and text editors. Try searching for a C-style cast in a large code and you'll see how hard this is.

On the other hand, C-style casts offer no advantages over C++ casts so there's not even a trade-off to consider.

More generally, Scott Meyers advises to “Minimize casts” in Effective C++ (item 27), because “casts subvert the type system.”

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • 1
    True about minimizing casts. But seriously can you point out any reason not to do float y = 1.0; double x = double(y)? Because *that* wsa the question. This cast is not dangerous and probably should _not_ distract compared to the real dangerous ones (which you almost never need if you do it right).. – ypnos Feb 13 '09 at 23:27
  • 3
    Well, in that very specific case, the cast is not even needed as double is a larger type than float. – Raphaël Saint-Pierre Apr 17 '09 at 17:04
  • I do not believe that any of these reasons (except *arguably* "should be ugly so they stand out", which I disagree with in general) apply to casts *among numeric types*; see my answer. – zwol Sep 07 '12 at 16:24
  • @Zack They don’t. But it’s much better to have a universally applicable, simple rule than one with just this one odd exception. Especially since there’s essentially no disadvantage to just always apply the guideline. – Konrad Rudolph Sep 07 '12 at 20:43
  • I guess we have a fundamental philosophical disagreement: I don't believe in universally applicable, simple rules. Or rather, I believe that they always eventually turn out to be wrong. I listed disadvantages of applying new-style casts to numbers in detail in my answer. – zwol Sep 08 '12 at 13:29
  • @Zack That’s not the misunderstanding: I know that no simple rules exist. It’s precisely for that reason that we need to simplify as much as we can. It’s important to minimise casts as much as possible in C++. That said, your post makes a few good points. Here, have a +1 to counter-act the -1. – Konrad Rudolph Sep 08 '12 at 14:15
  • 1
    "casts subvert the type system" sounds quite strange. I wouldn't say that a function like `int convertDoubleToInt(double)` subverts type system. But this is pretty much what `static_cast(myDouble)` does. What does subvert the type system is `const_cast` and `reinterpret_cast`, but not the other two casts. – Ruslan Jul 03 '17 at 12:48
  • @Ruslan I’m quoting Scott Meyers here. It is true that the specific case `static_cast(some_double)` doesn’t subvert the type system. But a `static_cast` in general *can* subvert the type system, for instance by casting `void*` to `T*` (this subverts the type system because it doesn’t perform a value conversion; rather, it asserts something that the type system cannot verify). For *conversions* I would strongly advocate using the `T{obj}` syntax instead, since it makes the intention clearer. Alas, this isn’t foolproof either (`std::vector<…>{}` doesn’t perform conversion …). – Konrad Rudolph Jul 03 '17 at 15:22
  • You can't use `T{obj}` to perform narrowing conversion, can you? – Ruslan Jul 03 '17 at 16:14
  • @Ruslan No. Oversight on my part. I’d still advocate `T{}` for non-narrowing conversions. Narrowing conversions probably deserve a `static_cast` to make them stand out anyway. – Konrad Rudolph Jul 03 '17 at 16:33
19

I would not, for the following reasons:

  • Casts are ugly and should be ugly and stand out in your code, and be findable using grep and similar tools.
  • "Always use C++ casts" is a simple rule that is much more likely to be remembered and followed than, "Use C++ casts on user-defined types, but it's OK to use C-style casts on built-in types."
  • C++ style casts provide more information to other developers about why the cast is necessary.
  • C-style casts may let you do conversions you didn't intend -- if you have an interface that takes in (int*) and you were using c-style casts to pass it a const int*, and the interface changes to take in a long*, your code using c-style casts will continue to work, even if it's not what you wanted.
Emil Laine
  • 41,598
  • 9
  • 101
  • 157
JohnMcG
  • 8,709
  • 6
  • 42
  • 49
  • 3
    I do not believe that any of these reasons (except *arguably* the first one) apply to casts *among numeric types*; see my answer. – zwol Sep 07 '12 at 16:24
10

If you are casting from a numeric type, to another numeric type, then I think C-style casts are preferable to *_cast. Each of the *_cast operators has a specific reason not to use it on numeric types:

  • reinterpret_cast, when applied to numeric types, does a normal numeric conversion rather than reinterpreting the bits, i.e. writing reinterpret_cast<uint64_t>(3.14159) does not produce the integer with the same bit representation as that floating-point constant. This is contrary to intuition.
  • const_cast is never necessary on a numeric value, and if applied to a numeric variable (as opposed to a pointer or reference to a number), suggests that the type of that variable is incorrect.
  • dynamic_cast just plain doesn't make sense on numbers because numbers never have dynamic type.
  • static_cast is normally used on class types. Therefore it looks strange to apply static_cast to a number; your readers will scratch their heads and wonder if there's something they don't know about static_cast that makes it different from the C-style cast when applied to a number. (In fact, they are identical, but I had to read the relevant C++ spec section a couple times to be sure they were identical, and I still have the "something weird must be going on here" reaction.)

and there is an additional stylistic reason to avoid them:

  • If you need to cast among numeric types, you are likely to need to do several of them in a cluster; because of that, the concision of C-style casts is important. For instance, the program I'm writing right now has a whole bunch of this sort of thing:

    uint32_t n = ((uint32_t(buf[0]) << 24) |
                  (uint32_t(buf[1]) << 16) |
                  (uint32_t(buf[2]) <<  8) |
                  (uint32_t(buf[3])      ));
    

    Using static_cast here would obscure the arithmetic, which is the important thing. (Note: those casts are unnecessary only if sizeof(uint32_t) <= sizeof(unsigned int), which is a safe assumption nowadays but I still prefer the explicitness.) (Yes, I probably ought to factor out this operation into a be32_to_cpu inline helper, but I'd still code it the same way.)

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Your assertion that static_cast is identical to c-style casts is incorrect. A C-style cast can resolve to reinterpret_cast, const_cast or static_cast depending on the situation. – PeterSW Sep 04 '13 at 16:24
  • 5
    @PeterSW C-style cast is identical to `static_cast` *for conversions among numeric types*. – zwol Sep 04 '13 at 19:42
  • 1
    If you're casting to an integer type that can hold a pointer then using static_cast can save you from an accidental `reinterpret_cast` hiding as a C-style cast. That would happen should the type you're casting be a pointer that you thought was an integer type. Hmm, having written that I feel I'm clutching at straws a little... before we started this discussion I was thinking there were more potential programmer errors that could be caught when casting to a numeric type. Now I'm thinking the case I just described is the only one. – PeterSW Sep 04 '13 at 21:43
  • 5
    @PeterSW My recommendations here apply only when *both* source and destination are unambiguously numeric types in context. If there's a pointer potentially involved, I agree the new-style cast operators are safer. – zwol Sep 04 '13 at 21:51
  • 2
    *"`static_cast` is normally used on class types. Therefore it looks strange to apply `static_cast` to a number;"* This sounds like a personal expectation and doesn't seem grounded in any kind of fact. – François Andrieux Nov 11 '20 at 15:58
3

I can think of one legitimate use for a C-style cast:

// cast away return value to shut up pedantic compiler warnings
(void)printf("foo\n");
MSeifert
  • 145,886
  • 38
  • 333
  • 352
tragomaskhalos
  • 2,733
  • 2
  • 17
  • 10
3

In my opinion C-Style casting of built in types when using standard library functions and STL is ok, and results in easier to read code.

In the company I work in we compile with maximum (level 4) warnings, so we get warnings about every little type cast etc... So I use c-style casts in these because they're small, not so verbose and make sense.

for (int i = 0; i < (int)myvec.size(); i++)
{
  // do something int-related with i
}
float val = (float)atof(input_string);

etc....

But if its on (eg library) code that may change, then static_cast<>() is better because you can ensure the compiler will error out if types change and the cast no longer makes sense. Also, its impossible to search for casts within code if you only use c-style. "static_cast<mytype>(" is pretty easy to search for. :)

user379529
  • 31
  • 2
2

I think it may be OK given the context. Example:

/* Convert -1..1 to -32768..32767 */
short float_to_short(float f)
{
    return (short)(max(-32768.f, min(32767.f, f * 32767.f)));
}
Johan Kotlinski
  • 25,185
  • 9
  • 78
  • 101
0

The one case where I prefer legacy C cast is casting byte buffers to different signedness. Many APIs have different conventions, and there is no "right answer" really, and the cast is not dangerous in the context it is done, where code needs to be only as platform-agnostic as the combination of libraries being used.

A concrete example for what I mean, I think this is just fine:

unsigned char foo[16];
lib1_load_foo(key);
lib2_use_foo((char*)key);

But for anything else, if cast is needed, it is going to have potential side-effects, which should stand out, and using C++ style (arguably ugly) cast is the right choice. And if cast is not needed, then don't use cast.

hyde
  • 60,639
  • 21
  • 115
  • 176
0

Why do you need that particular cast? Any numeric type can be converted to a long without a cast (at potential loss of precision), so casting doesn't let the compiler do anything it can't already. By casting, all you do is remove the compiler's ability to warn if there is a potential problem. If you're converting some other basic type (like a pointer), to a long, I'd really like to see a reinterpret_cast<> rather than a C-type cast, so I can find what's going on easily if there turns out to be a problem.

I'm not going to approve of casting without a reason, and I'm certainly not going to approve of C-type casts without a good reason. I don't see a reason to cast between most built-in types, and if there is one I want to be able to find it easily with a text search.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
  • If you don't want the compiler to generate loss-of-precision errors then casting is necessary – Michael Platings Feb 10 '09 at 11:39
  • Sure, but you may be better off suppressing them for now. When we went to 64-bit processing (needed more memory space), we would have lost precision in some cases (involving size_t) if we'd been casting. – David Thornley Feb 10 '09 at 14:37
-1

If you find you need to do a C-style cast (or a reinterpret_cast) then look very carefully at your code it is 99.99% certain there is something wrong with it. Both these casts leed almost inevitably to implementation specific (and very often undefined) behaviour.

  • "If you lie to the compiler, it will punish you". These casts are either a lie, or an attempt to fix a previous lie. – Darron Feb 09 '09 at 15:04
  • 4
    In research code I see often dealing with float and double side by side, or e.g. int data from an image getting casted to float/double for further processing. These are real use cases for a cast and I think they are more than 0.01%. – ypnos Feb 13 '09 at 23:30
  • What about parameters to thread functions on Windows? You have to pass void*. Common pattern is to pass reinterpret_cast(this). – Dave Mooney Apr 17 '09 at 16:50
  • 3
    Statements like this answer make me wonder if people have ever written real code before. Casts between floating point types (even when you are trying to avoid them because they are shockingly slow), signed and unsigned ints, ints and whatever godawful type vector::size() returns these days, etc. are absolutely unavoidable and perfectly normal in actual, practical code, particularly when you are gluing together libraries with APIs outside of your control. – user168715 Apr 14 '15 at 08:32