Is there any real different between using an union between two types and a reinterpret_cast between two types, for example--

#include <iostream>

int main(){
    union{
        unsigned short *a;
        float *b;
    };

    float value = 0.6f;
    b = &value;

    std::cout << *a << std::endl;

    unsigned short *s;
    float *f;

    f = &value;
    s = reinterpret_cast< unsigned short*>(f);

    std::cout << *s << std::endl;
    return 0;
}

--the results of the test are the same between the union and the reinterpret_cast but I would like to know if there is some kind of difference I should be aware of when using both.

Thank you!

-Alex

Recommended Answers

All 3 Replies

The only thing Edward can think of is using a union like that isn't required to work even if the conversion is safe but a reinterpret_cast<> is. The rules say that assigning to one member of a union and then accessing a different member right away is undefined behavior.

commented: Whoo! You rock! XD +3

Some additions:
In most of C++ implementations reinterpret_cast do nothing (like formally incorrect union trick, see Radical Edward's note). If you want to make a trick, do it honestly (it is open to question, of course ;)). It follows from this that reinterpret_cast is "better" than union trick (in the spirit of C++ language).

I know (old) computers where pointers are hardware-typed, so you can't refer to an integer via pointer to float (and vice versa). Probably, on such computers C++ compiler (in theory) can generate a proper conversion code for reinterpret_cast (but not for unions, of course). A very good compiler is capable to generate a proper run-time check up of a good object alignment (in debug mode, for example) for reinterpret_cast - and so on.

Apropos, reinterpret_cast for pointers has C++-defined result only if you previously assigned a pointer value to void* then reinterpret_cast this void* value to the same type!

Moral: use reinterpret_cast where possible, but a trick is a trick...

commented: Thank you! =) +3

> Is there any real different between using an union between two types and a reinterpret_cast between two types
yes, there are quite a lot of differences.
a union reinterprets the layout of memory (depending on which member is accessed).
a reinterpret_cast is a conversion operator.

the conversions that can be performed using a reinterpret_cast are limited; the permissible ones are specified by the standard.

IS 5.2.10 - Reinterpret cast

-1- The result of the expression reinterpret_cast<T>(v) is the result of converting the expression v to type T.
...
Conversions that can be performed explicitly using reinterpret_cast are listed below. No other conversion can be performed explicitly using reinterpret_cast.
-2- The reinterpret_cast operator shall not cast away constness. ...
-3- The mapping performed by reinterpret_cast is implementation-defined. ...

and then it goes on to list the conversions that can be performed explicitly using reinterpret_cast. for example this is what is specified about pointer to integral and integral to pointer conversions:

-4- A pointer can be explicitly converted to any integral type large enough to hold it. The mapping function is implementation-defined.
[Note: it is intended to be unsurprising to those who know the addressing structure of the underlying machine. ]
-5- A value of integral type or enumeration type can be explicitly converted to a pointer. A pointer converted to an integer of sufficient size (if any such exists on the implementation) and back to the same pointer type will have its original value; mappings between pointers and integers are otherwise implementation-defined. [Footnote: Converting an integral constant expression with value zero always yields a null pointer, but converting other expressions that happen to have value zero need not yield a null pointer. --- end foonote]

and this is what is specified about a reinterpret_cast of one pointer type to a pointer of a different type:

-7- A pointer to an object can be explicitly converted to a pointer to an object of different type.
[Footnote: The types may have different cv-qualifiers, subject to the overall restriction that a reinterpret_cast cannot cast away constness. --- end foonote]
Except that converting an rvalue of type ``pointer to T1'' to the type ``pointer to T2'' (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value, the result of such a pointer conversion is unspecified.

however, for pointers to POD types, both a union and a reinterpret_cast should give identical results:
IS 5.4/5 says "...barring const_cast, C-style cast is equivalent to reinterpret_cast, (unless static_cast is possible)"
IS 9.5/1 "The size of a union is sufficient to contain the largest of its data members. Each data member is allocated as if it were the sole member of a struct"
IS 9.2/17 "A pointer to a POD-struct object, suitably converted using a reinterpret_cast, points to its initial member [...] and vice versa."

taken together, this seems pretty clear - these constrain the implementation of pointer reinterpret_cast; the source and destination pointer shall point to the same object (have the same value), unless this is impossible due to alignment requirements. even if they have different values (due to alignment requirements), the union and reinterpret_cast would give identical results.

an example showing some of the differences between union and reinterpret_cast:

#include <iostream>

int main()
{
  union
  {
    short s ;
    int i ;
    double d ;
    char bytes[ sizeof(double) > sizeof(int) ? sizeof(double)
                                             : sizeof(int) ] ;
  } ;

  i = 123456789 ; // sets first sizeof(int) bytes of the union

  // ok, interpret the first sizeof(int) bytes as an int
  std::cout << "i: " << i << '\n'
  // ok, interpret the first sizeof(double) bytes as a double
            << "d: " << d << '\n'
  // ok, interpret the first sizeof(short) bytes as a short
            << "s: " << s << '\n'
  // ok, interpret the third byte as a char
            << "bytes[3]: " << bytes[3] << '\n' ;


  // ok, reinterpret reference to int as reference to double
  std::cout << reinterpret_cast<double&>(i) << '\n' ;
  // ok, reinterpret reference to int as reference to short
  std::cout << reinterpret_cast<short&>(i) << '\n' ;

  // ok, reinterpret pointer to int as pointer to double
  std::cout << *reinterpret_cast<double*>(&i) << '\n' ;
  // ok, reinterpret pointer to int as pointer to short
  std::cout << *reinterpret_cast<short*>(&i) << '\n' ;

  // ok, pd gets the (numeric) value of the int
  double* pd = reinterpret_cast<double*>(i) ;
  // ok, pd gets the (numeric) value of the short
  pd = reinterpret_cast<double*>(s) ;

  // error, no such reinterpret_cast is possible fom int to double
  // std::cout << reinterpret_cast<double>(i) << '\n' ;

  // error, no such reinterpret_cast is possible
  // std::cout << reinterpret_cast<double>(s) << '\n' ;

}
commented: Thanks for the elaborate explanation! +3
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.