41

What exactly is a type cast in C/C++? How does the compiler check if an explicit typecast is needed (and valid)? Does it compare the space required for an value? If I have for example:

int a;
double b = 15.0;
a = (int) b;

If I remember correctly a double value requires more space (was it 8 bytes?!) than an integer (4 bytes). And the internal representation of both are completely different (complement on two/mantissa). So what happens internally? The example here is quite straightforward, but in C/C++ there are plentiful typecasts.

How does the compiler know (or the programmer) if I can cast e.g. FOO to BAR?

Amjad Rehman A
  • 788
  • 10
  • 21
pluckyDuck
  • 1,099
  • 3
  • 13
  • 14
  • 7
    Any complete explanation of type casts needs to include standard conversions, user-defined conversions, `reinterpret_cast`, `const_cast`, upcasts, downcasts, cross-casts, RTTI, and value types. (And I think I forgot a topic.) – Ben Voigt Sep 26 '11 at 17:38
  • possible duplicate of [When should static_cast, dynamic_cast and reinterpret_cast be used?](http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-and-reinterpret-cast-be-used) – Mark B Sep 26 '11 at 17:50
  • @Ben, I think I mentioned the vast majority of them in my humble answer, but no (reasonably concise) answer can possibly be considered complete. – Michael Goldshteyn Sep 26 '11 at 17:59
  • @MichaelGoldshteyn: Your answer edit hadn't shown up yet when I wrote that, there were just two very brief answers that focused on double->int conversions. – Ben Voigt Sep 26 '11 at 18:02
  • @Ben, yes it took a while to enhance my answer with more detail perhaps than was asked for. But, I came to the same realization as you and decided that the detail was necessary to even attempt to come up with a somewhat complete answer to the general question. – Michael Goldshteyn Sep 26 '11 at 18:07

4 Answers4

32

A type cast is basically a conversion from one type to another. It can be implicit (i.e., done automatically by the compiler, perhaps losing info in the process) or explicit (i.e., specified by the developer in the code). The space occupied by the types is of secondary importance. More important is the applicability (and sometimes convenice) of conversion.

It is possible for implicit conversions to lose information, signs can be lost / gained, and overflow / underflow can occur. The compiler will not protect you from these events, except maybe through a warning that is generated at compile time. Slicing can also occur when a derived type is implicitly converted to a base type (by value).

For conversions that can be downright dangerous (e.g., from a base to a derived type), the C++ standard requires an explicit cast. Not only that, but it offers more restrictive explicit casts, such as static_cast, dynamic_cast, reinterpret_cast, and const_cast, each of which further restricts the explicit cast to only a subset of possible conversions, reducing the potential for casting errors.

Valid conversions, both implicit and explict are ultimately defined by the C/C++ standards, although in C++, the developer has the ability to extend conversions for user defined types, both implicit and explicit, via the use of constructors and overloaded (cast) operators.

The complete rules for which casts are allowed by the standards and which are not can get quite intricate. I have tried to faithfully present a somewhat concise summary of some of those rules in this answer. If you are truly interested in what is and is not allowed, I strongly urge you to visit the standards and read the respective sections on type conversion.

Michael Goldshteyn
  • 71,784
  • 24
  • 131
  • 181
  • 1
    "_it offers more restrictive explicit casts, such as static_cast, dynamic_cast, reinterpret_cast, and const_cast_" True, but a cast to a floating point type is always a `static_cast`. You do not need the verbosity of these differentiated casts here. – curiousguy Sep 27 '11 at 12:26
9

Just want to mention something frequently overlooked:

  • A cast always creates a temporary of the target type (although if the target type is a reference, you won't notice).

This can be important. For example:

#include <iostream>

void change_one_print_other( int& a, const int& b )
{
    a = 0;
    std::cout << b << "\n";
}

int main(void)
{
    int x = 5, y = 5;

    change_one_print_other(x, x);
    change_one_print_other(y, static_cast<int>(y));
}

That cast LOOKS useless. But looks can be deceiving.

GingerPlusPlus
  • 5,336
  • 1
  • 29
  • 52
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • "_A cast always creates a temporary of the target type (although if the target type is a reference, you won't notice)_" If the target type is a reference, than the cast will not create a temporary; it will make a lvalue. `void change_one_print_other( int& a, const int& b ); change_one_print_other(y, static_cast(y));` If you want to avoid this mistake (passing a reference to a temporary), you need to take a pointer: `void change_one_print_other( int& a, const int* pb );` To everybody here: I am new to this system, sorry if I don't use this site's features correctly. – curiousguy Sep 27 '11 at 12:06
  • Ben Voigt wrote: "Since references are transparent, you can't tell if a temporary reference was created or not. The temporary reference is bound to the same target, and yes it's an lvalue (unless it's an rvalue reference, of course)." (comment of a deleted post) – curiousguy Sep 27 '11 at 12:08
  • I meant to say that a temporary is never a reference, and a reference is never a temporary. Unless it changed, a temporary is a rvalue and a reference is a lvalue. This may be nitpicking, but IMO when dealing with C++ it is important to use the most precise vocabulary. – curiousguy Sep 27 '11 at 12:10
  • @curiousguy: `(*p)` is an lvalue, even when `p` is a temporary. You can think about a reference the same way, but since naming a reference always yields the value it refers to (the reference is totally transparent), it's impossible to tell whether a temporary reference was created. Just like you can't ask the compiler how big a reference is, you can only ask how big the target of the reference is. But that doesn't mean that the reference doesn't take up storage -- it frequently does. – Ben Voigt Sep 27 '11 at 14:05
  • Apparently, reinterpret_cast does not create this but, yes, static_cast does. – user1284631 Sep 17 '14 at 14:45
  • @Ben The link in your answer seems to be broken. – GingerPlusPlus Oct 14 '14 at 12:26
  • @GingerPlusPlus: Please complain to ideone.com (I already have). [Their policy](http://ideone.com/faq) is that they store code snippets "Forever." Anyway, the code example in my answer is complete, so you can just cut+paste it into your favorite compiler and see the effect of the cast. – Ben Voigt Oct 14 '14 at 13:03
1

There are certain type casts that the compiler knows how to do implicitly - double to int is one of them. It simply drops the decimal part. The internal representation is converted as part of the process so the assignment works correctly.

Note that there are values too large to be converted properly. I don't remember what the rules are for that case; it may be left at the discretion of the compiler.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • I think you meant to say "int to double", not the other way around. Compilers do warn of possible loss of data when assigning a double to an int. – Praetorian Sep 26 '11 at 17:35
  • 4
    @Praetorian, I don't think compiler warnings are specified by the standard - that's at the discretion of the compiler as well. Your description is correct for the compilers I've used though. – Mark Ransom Sep 26 '11 at 17:39
  • @Tomolak declines to answer? This kind of question is mother's milk to him :-) – Pete Wilson Sep 26 '11 at 17:46
  • I believe the standard does sometimes require some kind of diagnostic message (a.k.a., warning) but (1) leaves the wording up to the compiler and (2) allows compilers to have additional warnings (and the "possible information loss" warning from double to int would be one of these). – Max Lybbert Sep 26 '11 at 17:54
  • 2
    @MaxLybbert The C and C++ standards requires implementations to diagnose... errors that require a diagnostic. The documentation should describe what constitute a diagnostic. The implementation can also diagnose pretty much anything they want, like: "_It's 2 AM and you shouldn't do any more programming now._" or "_You are programming in C++, a very complicated programming language. You probably should consider learning a simpler language._" – curiousguy Sep 27 '11 at 11:53
  • "_There are certain type casts that the compiler knows how to do implicitly - double to int is one of them._" True. But it is considered a bad idea, which is the reason why: "_Compilers do warn of possible loss of data when assigning a double to an int._" You can probably disable this warning, but you really shouldn't. "_It simply drops the decimal part._" In many cases, you do not want to "drop the decimal part", but you need the nearest value, or the integer just below (`floor`), or the integer just above (`ceil`). – curiousguy Sep 27 '11 at 12:01
  • 1
    Even if you want to "drop the decimal part", floating point types can store much bigger integers than integer types. Because of the size of the mantissa, the typical implementation of double can represent exactly much bigger integers than a 32 bit integer type. If you want the decimal part, and you are sure that it will fit in an integer type, you should at last make this conversion as explicit as possible in the source code. – curiousguy Sep 27 '11 at 12:01
1

Make a little C program of your code and follow the instructions in How to get GCC to generate assembly code to see how the compiler does a type cast.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ott--
  • 5,642
  • 4
  • 24
  • 27
  • 3
    But it will only tell you what a given compiler, invoked with a given set of options, on a given architecture, does. There are many areas where C++ behaviour is allowed by the standard to vary between compilers (implementation defined, unspecified, undefined behaviours). Also, not all compilers perfectly implement every standard requirement. – curiousguy Sep 27 '11 at 12:14