What exactly is a type cast in C/C++?

23,274

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

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.

Share:
23,274
pluckyDuck
Author by

pluckyDuck

Updated on July 26, 2020

Comments

  • pluckyDuck
    pluckyDuck almost 4 years

    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?

  • Praetorian
    Praetorian over 12 years
    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.
  • Mark Ransom
    Mark Ransom over 12 years
    @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.
  • Pete Wilson
    Pete Wilson over 12 years
    @Tomolak declines to answer? This kind of question is mother's milk to him :-)
  • Max Lybbert
    Max Lybbert over 12 years
    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).
  • curiousguy
    curiousguy over 12 years
    @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
    curiousguy over 12 years
    "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
    curiousguy over 12 years
    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
    curiousguy over 12 years
    "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<int>(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
    curiousguy over 12 years
    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
    curiousguy over 12 years
    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
    curiousguy over 12 years
    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
    curiousguy over 12 years
    "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.
  • Ben Voigt
    Ben Voigt over 12 years
    @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.
  • user1284631
    user1284631 over 9 years
    Apparently, reinterpret_cast does not create this but, yes, static_cast does.
  • GingerPlusPlus
    GingerPlusPlus over 9 years
    @Ben The link in your answer seems to be broken.
  • Ben Voigt
    Ben Voigt over 9 years
    @GingerPlusPlus: Please complain to ideone.com (I already have). Their policy 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.