Consider the following program:
struct ghost
{
// ghosts like to pretend that they don\'t exist
ghost* operator&() const volatile { return 0; }
}
The trick behind boost::addressof and the implementation provided by @Luc Danton relies on the magic of the reinterpret_cast
; the standard explicitly states at §5.2.10 ¶10 that
An lvalue expression of type
T1
can be cast to the type “reference toT2
” if an expression of type “pointer toT1
” can be explicitly converted to the type “pointer toT2
” using areinterpret_cast
. That is, a reference castreinterpret_cast
has the same effect as the conversion(x) *reinterpret_cast
with the built-in(&x) &
and*
operators. The result is an lvalue that refers to the same object as the source lvalue, but with a different type.
Now, this allows us to convert an arbitrary object reference to a char &
(with a cv qualification if the reference is cv-qualified), because any pointer can be converted to a (possibly cv-qualified) char *
. Now that we have a char &
, the operator overloading on the object is no longer relevant, and we can obtain the address with the builtin &
operator.
The boost implementation adds a few steps to work with cv-qualified objects: the first reinterpret_cast
is done to const volatile char &
, otherwise a plain char &
cast wouldn't work for const
and/or volatile
references (reinterpret_cast
cannot remove const
). Then the const
and volatile
is removed with const_cast
, the address is taken with &
, and a final reinterpet_cast
to the "correct" type is done.
The const_cast
is needed to remove the const
/volatile
that could have been added to non-const/volatile references, but it does not "harm" what was a const
/volatile
reference in first place, because the final reinterpret_cast
will re-add the cv-qualification if it was there in first place (reinterpret_cast
cannot remove the const
but can add it).
As for the rest of the code in addressof.hpp
, it seems that most of it is for workarounds. The static inline T * f( T * v, int )
seems to be needed only for the Borland compiler, but its presence introduces the need for addr_impl_ref
, otherwise pointer types would be caught by this second overload.
Edit: the various overloads have a different function, see @Matthieu M. excellent answer.
Well, I'm no longer sure of this either; I should further investigate that code, but now I'm cooking dinner :) , I'll have a look at it later.