Re: Casting references through unions.
 
Steve Checkoway schrieb:
Is the cast in the following code valid?
template <typename TO, typename FROM>
TO &CastSimilarTypes( FROM &val )
{
    union conv { FROM from; TO to; };
    conv &u = (conv &)val;
    return u.to;
}
My guess is no. When I replace the c-style cast with static_cast<conv
&>(val), gcc chokes on it.
Normal. It's a reinterpret_cast. I don't see what the above function 
buys you over just writing
    reinterpret_cast<TO&>(val)
In particular, it won't work when sizeof(FROM) < sizeof(TO), or 'val' 
(the Lvalue of type FROM) is not properly aligned for storing an object 
of type TO, or when it contains a bit pattern that is invalid for TO.
I had the idea to change the code to
template <typename TO, typename FROM>
TO &CastSimilarTypes( FROM &val )
{
    union conv { FROM *from; TO *to; };
    conv u = { &val };
    return *u.to;
}
but I'm not sure that that is any more valid even though gcc seems to
allow it and there are no casts required.
Pointers to different types do not need to have the same size or object 
representations - on some implementations, you have
    sizeof(char*) > sizeof(int*)
Thus the situation is even worse than as for reinterpret_cast, since 
'to' does not even need to point to the same object as 'from' does.
Assuming that it isn't, the real issue is casting enums to int
references, mostly for functions that return values in parameters. For
example, I have been replacing the commented line of code with the code
that follows it:
void Foo( int &i ) { /* ... */ }
enum E { /* ... */ };
/* ... */
E e;
// Foo( (int &)e ); // Replacing this line with what follows.
int temp;
Foo( temp );
e = E( temp );
(int&)e is equivalent to reinterpret_cast<int&>(e) and is not guaranteed 
to work, because 'int' and 'E' do not need to have the same size - think 
about what happens when 'E' comports enumerators initialized to values 
that do not fit in an 'int'. OTOH, a compiler could choose signed or 
unsigned 'char' or 'short' as the underlying type of 'E' provided all 
enumerator values can be represented within this type.
The second variant (using 'int temp') is OK, unless the value of 'temp' 
happens to be out of range for 'E', in which case the result is 
unspecified (but at least the behavior is not undefined). The three 
lines can be replaced by one with the following:
    template<typename DstT, typename SrcT> class ref_conv_helper
    {
      SrcT&  src;
      DstT   dest;
    public:
      explicit ref_conv_helper(SrcT& s) : src(s), dest() {}
      operator DstT&() { return dest; }
      ~ref_conv_helper()
      {
        // Check if dest is within range for SrcT?
        src = static_cast<SrcT>(dest);
      }
    }; // class ref_conv_helper
    template<typename DstT, typename SrcT> inline
    ref_conv_helper<DstT, SrcT> ref_convert(SrcT& src)
    { return ref_conv_helper<DstT, SrcT>(src); }
Now you are able to write
    Foo(ref_convert<int>(e));
where the source type ('E' in your example) is automatically deduced for 
instantiating ref_convert. The life time of the temporary of type 
ref_conv_helper<int, E> extends to the end of the full expression 
(comprising the call to Foo()) and its destructor writes the result into 
'e'.
FWIW, it is even possible to let the compiler deduce the destination 
type, letting you write
    Foo(ref_convert(e));
However, there are two important drawbacks: It will fail if Foo() is 
overloaded/templatised for several parameter types, and it has much more 
overhead (dynamic allocation + virtual dispatch, unless somebody comes 
up with a better solution). Anyway, here is how to do it:
    template<typename SrcT> class ref_conv_helper
    {
      struct wrapper_base
      {
        virtual ~wrapper_base() {}
        virtual void get_src(SrcT&) = 0;
      }; // struct wrapper_base
      template<typename DstT> struct wrapper : public wrapper_base
      {
        DstT dest;
        wrapper() : dest() {}
        void get_src(SrcT& src)
        {
          // Check if dest is in range for SrcT?
          src = static_cast<SrcT>(dest);
        }
      }; // struct wrapper
      wrapper_base* ptr;
      SrcT&         src;
    public:
      explicit ref_conv_helper(SrcT& s) : ptr(0), src(s) {}
      template<typename DstT> operator DstT&()
      {
        wrapper<DstT>* p = new wrapper<DstT>;
        ptr = p;
        return p->dest;
      }
      // Without the following, GCC generates compilation errors since
      // it tries to instantiate above conversion operator with DstT =
      // 'ref_conv_helper const'.
      // Comeau doesn't require this (and even warns that this operator
      // will not be called for implicit or explicit conversions).
      operator ref_conv_helper const&() { return *this; }
      ~ref_conv_helper()
      {
        if(ptr != 0)
          ptr->get_src(src);
        delete ptr;
      }
    }; // class ref_conv_helper
    template<typename SrcT> inline
    ref_conv_helper<SrcT> ref_convert(SrcT& src)
    { return ref_conv_helper<SrcT>(src); }
HTH
Falk
-- 
      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]