8

I do not believe that it is possible to completely avoid C-style casts when writing C++. I was surprised to find out that I needed to use a C-style cast to avoid a compiler truncation warning:

short value_a = 0xF00D;                     // Truncation warning in VS2008
short value_b = static_cast<short>(0xF00D); // Truncation warning in VS2008
short value_c = (short)0xF00D;              // No warning!

Are there other scenarios where there is no C++-style substitute for a C-style cast?

Community
  • 1
  • 1
sourcenouveau
  • 29,356
  • 35
  • 146
  • 243
  • 7
    `short value_c(0xF00D)` -- also: which compiler is giving you truncation warnings? My GCC is smart enough not to in all three situations. – Travis Gockel Nov 18 '10 at 20:34
  • C++ casts cover for all the C cast operator uses. – wilhelmtell Nov 18 '10 at 20:36
  • 11
    As far as the language cares, there's no difference between `static_cast` and a C-style cast in this case. If the compiler decided to give a warning that "it's 3AM local time -- you should really go to bed", it's free to do that though... – Jerry Coffin Nov 18 '10 at 20:37
  • 1
    @TravisGockel that may or may not issue a warning about truncation. If `sizeof(short)` is 1 then this will truncate. – wilhelmtell Nov 18 '10 at 20:38
  • 1
    @wilhemltell: not so. See: http://stackoverflow.com/questions/2346616/need-clarifications-in-c-style-reinterpret-and-const-casts/2346658#2346658 – Jerry Coffin Nov 18 '10 at 20:38
  • 1
    What compiler are you using? **I don’t get any warning** even with `g++ -Wall -Wextra` (where `sizeof(int) == 4` and `sizeof(short) == 2`. Of course if that’s not the case the compiler is warning for a good reason, and you should *not* try to shut it up!) – Konrad Rudolph Nov 18 '10 at 20:39
  • @wilhelmtell: In those cases, you wouldn't *want* the warning to go away. If implicit constant conversion is going to screw up, that is a great warning to have. – Travis Gockel Nov 18 '10 at 20:41
  • Which compiler produces this error? IMO, `static_cast` is meant for such cases... – UncleBens Nov 18 '10 at 20:42
  • @TravisGockel maybe yes, maybe not. What if the value is guaranteed to be low enough? Then you **don't** want to see the warning. Dummy warnings hide **real, important, relevant** warnings. – wilhelmtell Nov 18 '10 at 20:43
  • 1
    @UncleBens: While errors and warning are both _diagnostics_, they aren't the same. The OP got a _warning_, not an _error_. The standard doesn't care, but in practice this is quite a difference. – sbi Nov 18 '10 at 20:47
  • Just throwing this out there: **the result of `reinterpret_cast` is implementation-defined**. To say "A C-style cast can be replaced with a `reinterpret_cast`" is just bogus; C-style casts aren't necessarily implementation-defined. (Unless you're `reinterpret_cast`'ing a POd to `char&`, etc, yadda-yadda.) – GManNickG Nov 18 '10 at 20:47
  • @wilhelmtell: That means we agree on my original point -- the compiler should be smart enough to figure this out and not issue a warning here. OP is not using GCC or LLVM, since neither of them issue a warning here. – Travis Gockel Nov 18 '10 at 20:52
  • @sbi: Sorry, I meant *warning*. It doesn't seem to be the case with VC++ either. – UncleBens Nov 18 '10 at 20:55
  • 5
    0xF00d can not fit in short because it is larger than max_short (0x7FFF). Use unsigned short instead. – Dialecticus Nov 18 '10 at 20:58
  • @Dialecticus: According to the standard, there can be platforms out there where a `short` is 47 bits wide. – sbi Nov 18 '10 at 21:03
  • 4
    @sbi: Of course, but in this particular case maximum short is 0x7FFF, hence the warning. – Dialecticus Nov 18 '10 at 21:06

6 Answers6

13

In C++, the C-style cast is defined (§5.4) in terms of C++-style casts. So for every cast you can do C-style, there's a matching C++-style cast (almost).

The "almost" is that C-style casts ignore base class accessibility. That is, there is no equivalent C++-style cast for the following:

struct foo {};
struct bar : private foo {};

bar b;
foo* f = (foo*)&b; // only way this can be done in a well-defined manner

So, no it's not strictly-speaking possible to completely ditch C-style casts. But the number of areas where a (combination of) C++-style casts doesn't suffice is few in count.


The above is the "language answer". What you're experiencing has nothing to do with C-style casts versus C++ casts, but just compiler implementation. Warnings are absolutely implementation-specific, and have nothing to do with C++.

So don't make the mistake of using your findings on this particular compiler in this particular situation for concluding things about C++ in general.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • `foo* f = reinterpret_cast(&b)` ? Although I'm assuming you forgot the `&` from `foo* f = (foo*)b`. – Travis Gockel Nov 18 '10 at 20:43
  • @Travis: Nope. The result of `reinterpret_cast` is **implementation-defined**. With the C-style cast, it acts like a `static_cast` which is not implementation-defined. – GManNickG Nov 18 '10 at 20:45
  • @GMan: clearly, it does *not* act like a `static_cast` in the OP’s case. Otherwise it would give a warning. Since there is none, I conclude that it acts like the `reinterpret_cast` here. Or the compiler has a bug. Take your pick. – Konrad Rudolph Nov 18 '10 at 20:48
  • 2
    +1 for not using the compiler to tell you what is truth according to the standard. – John Dibling Nov 18 '10 at 20:59
  • "but you'd be hard-pressed to find a good situation where it's needed.". CRTP with a private or protected base class is definitely a good use-case of it. I've used it for that, together with `boost::is_base_of` (which can detect private inheritance) for ensuring inheritance. – Johannes Schaub - litb Nov 18 '10 at 21:15
  • @Konrad: Huh? Where did I say anything about it acting like `static_cast` for OP? Warnings and compliance have nothing to do with each other, so you should rethink your argument. I could have a fully compliant compiler that emits a warning every other line because I felt like it. @sbi: Urg, typo. @Johannes: Fair, I've weakend the statement a bit. – GManNickG Nov 18 '10 at 21:23
  • @GMan: What else did you refer to, if not OP’s code? Your example (private inheritance) doesn’t even compile when using `static_cast` so I assumed you weren’t referring to that. – Fair comment about the warning (although I would *still* think that warning about a well-defined operation is borderline to a bug). – Konrad Rudolph Nov 18 '10 at 21:36
  • @Konrad: Can you clarify what you mean by "refer to"? What text are you seeing? The one to Travis? We were talking about my snippet, which cannot be replaced with `static_cast` but acts like one. In other words, using the C-style cast there is like making the base public instead of private, then using `static_cast`. – GManNickG Nov 18 '10 at 21:41
  • @GMan: that clears it up (“cannot be replaced with … but acts like one“). I misconstrued what you meant by “acts like” above. – Konrad Rudolph Nov 18 '10 at 21:48
4

You are just trying to obfuscate your code, it is as simple as that. And the compiler is completely correct in telling you so.

If you have a precise idea what the assigned value should be, use that. My guess is that you have some unfounded presumption of short being 16 bit wide and that the sign representation of the target machine is two's complement. If that is so, assign -4083 to your variable. If you just need your variable as a bit vector, use an unsigned type.

As far as C is concerned the standard simply says about conversion from one integer type to another:

Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.

I imagine the the point of view of C++ with this respect is not much different. Other answers mention border cases where in C++ you would need a `C'-style cast to overrule all typechecks that C++ gives you. Feeling the need for them is an indication of bad design.

The case that you give as an example is certainly not one for which I would find any valid circumstances.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
1

Yes, it is completely possible.

I never use C-style casts. I can write hundreds of thousands of lines of code without having to revert to using reinterpret_cast, C++'s closest cousin to the C-style cast. The only times I have to use reinterpret_cast is when doing socket programming -- a fairly narrow domain, in the big picture.

You don't need to use C-style casts, either. In your other post, you said

I could just use a negative value,

short my_value = -4083;

but in my code it is much more understandable to use hexadecimal.

So in this case you didn't have to use the cast. You chose to.

0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Recently, I had to use a reinterpret_cast to convert from a raw unsigned char array to an instance of a struct. Is this something like what you were saying, about the sockets? – San Jacinto Nov 18 '10 at 20:46
  • @San. Maybe. :) I'd have to see it. – John Dibling Nov 18 '10 at 20:50
  • 2
    I'd up-vote to counter the inexplicable down-vote, but I ran out of votes for today. `:(` But, yeah, for me, too, the LoC/`reinterpret_cast` ration is somewhere in the six-digit numbers, and I don't think I've used a c-style cast in C++ code in the last decade. – sbi Nov 18 '10 at 20:59
  • @sbi: I must have a random hater. – John Dibling Nov 18 '10 at 21:11
  • Might be better than a random lover. `:)` – sbi Nov 18 '10 at 21:38
1

There are 4 c++ style casts, const_cast, reinterpret_cast, static_cast and dynamic_cast. They work as follows:

// const_cast casts away constness or adds it
const int const_integer = 5;
const_cast<int>(const_integer) = 3;

// static_cast will perform standards defined casts and will 
// cast up or down a c++ inheritance hierarchy without checking the result of the cast
struct b {};
struct a : public b {};
struct c {};
double value = static_cast<double>(0.0f);
b* b_value = new b;
a* a_value = static_cast<a*>(b_value);

// dynamic_cast will perform any cast that static_cast will, but will check to see
// if the cast makes sense. If the values are not pointers, this cast can throw
b* value_b = new b;
a* value_a = new a;
b* new_b = dynamic_cast<b*>(value_a); // will return NULL
a* new_a = dynamic_cast<a*>(value_b); // will not return NULL    

// reinterpret_cast will change any type to any other type, as long as the constness of the types is the same.
// the behavior of this cast is implementation specific.
double* a = new double;
*a = 0.0f;
int *b = reinterpret_cast<int*>(a);

A c-style cast in c++ simply tries to perform those casts in a specific order until one of them works. That order is as follows:

  • a const_cast
  • a static_cast
  • a static_cast followed by a const_cast
  • a reinterpret_cast, or
  • a reinterpret_cast followed by a const_cast.

So, in short, you can do any c-style cast in c++, because a c-style cast in c++ is just some arrangement of c++ style casts. Get it?

tyree731
  • 453
  • 3
  • 8
0

I was surprised to find out that I needed to use a C-style cast to avoid a compiler truncation warning

I see it the other way around: You use a C-style cast to prevent the compiler from warning you, and I see this as a severe disadvantage of the C-style cast.

if you feel you know what you're doing, then shut up the compiler for this one case by using a compiler-specific way. For example, for VC use something like

#pragma warning(push, disable: XXXX)
// code goes here
#pragma warning(pop)
sbi
  • 219,715
  • 46
  • 258
  • 445
  • I would never have thought to do this, probably because we use at least 3 different compilers/compiler families to compile the same code for different targets, and a cast seems cleaner to me than doing all of the preprocessor checking to determine the compiler each time I could have just used a cast. What is your thought? – San Jacinto Nov 18 '10 at 20:49
  • 2
    @San: I have been part of a project were most of the code was compiled by half a dozen different compilers and versions thereof, times several different std lib implementations. Still, most of us tried to avoid using C-style casts, because they shut up the compiler completely, preventing it from pointing out errors. (And every error found through compiling is worth 1000 found at run-time, usually by a customer.) IME experience __different compilers will vary wildly in what they warn about__ anyway, so usually you need to shut them up (selectively) at different places. – sbi Nov 18 '10 at 20:54
  • Make that `#pragma warning(suppress: XXXX)` perhaps? – Matt Joiner Nov 19 '10 at 05:54
  • @Matt: That `suppress` is something I hadn't heard about yet. – sbi Nov 19 '10 at 06:15
-3

In these cases, you can use reinterpret_cast. It was meant for a replacement of a unchecked C-style cast. The typical note here: this is unchecked cast, and should be avoided when possible by using the other available: const_cast, dynamic_cast, etc.

Diego Sevilla
  • 28,636
  • 4
  • 59
  • 87
  • 1
    Actually, C-style cast can be one of 5 combinations of `static_cast`, `const_cast` and `reinterpret_cast`. – kennytm Nov 18 '10 at 20:38
  • Good answer..blah, blah, blah... yadda, yadda... don't use this mehod unless you absolutely have to. – San Jacinto Nov 18 '10 at 20:38
  • 12
    I don't think a `reinterpret_cast` should be use to truncate an `int` to a `short`. Ever. – sbi Nov 18 '10 at 20:39