Implementation of addressof
- First you have
__r
which is of type_Tp&
- It is
reinterpret_cast
'ed to achar&
in order to ensure being able to later take its address without fearing an overloadedoperator&
in the original type; actually it is cast toconst volatile char&
becausereinterpret_cast
can always legally addconst
andvolatile
qualifiers even if they are not present, but it can't remove them if they are present (this ensures that whatever qualifiers_Tp
had originally, they don't interfere with the cast). - This is
const_cast
'ed to justchar&
, removing the qualifiers (legally now!const_cast
can do whatreinterpret_cast
couldn't with respect to the qualifiers). - The address is taken
&
(now we have a plainchar*
) - It is
reinterpret_cast
'ed back to_Tp*
(which includes the originalconst
andvolatile
qualifiers if any).
Edit: since my answer has been accepted, I'll be thorough and add that the choice of char
as an intermediate type is due to alignment issues in order to avoid triggering Undefined Behaviour. See @JamesKanze's comments (under the question) for a full explanation. Thanks James for explaining it so clearly.
It's actually quite simple when you think about it, to get the real adress of an object/function in precense of an overloaded operator&
you will need to treat the object as something other than what it really is, some type which cannot have an overloaded operator.. an intrinsic type (such as char
).
A char
has no alignment and can reside anywhere any other object can, with that said; casting an object to a reference to char is a very good start.
But what about the black magic involved when doing reinterpret_cast<const volatile char&>
?
In order to reinterpret the returned pointer from the implementation of addressof
we will eventually want to discard qualifiers such as const
and volatile
(to end up with a plain reference char
). These two can be added easily with reinterpret_cast
, but asking it to remove them is illegal.
T1 const a; reinterpret_cast<T2&> (a);
/* error: reinterpret_cast from type ‘...’ to type ‘...’ casts away qualifiers */
It's a little bit of a "better safe than sorry" trick.. "Let us add them, just in case, we will remove them later."
Later we cast away the qualifiers (const and volatile) with const_cast<char&>
to end up with a plain reference to char
, this result is, as the final step, turned back into a pointer to whatever type we passed into our implementation.
A relevant question on this stage is why we didn't skip the use of reinterpret_cast
and went directly to the const_cast
? this too has a simple answer: const_cast
can add/remove qualifiers, but it cannot change the underlying type.
T1 a; const_cast<T2&> (a);
/* error: invalid const_cast from type ‘T1*’ to type ‘T2*’ */
it might not be easy as pie, but it sure tastes good when you get it..
The short version:
operator&
can't be overloaded for char
. So the type is being cast to a char
reference to get what's guaranteed to be the true address.
That conversion is done in two casts because of the restrictions on const_cast
and reinterpret_cast
.
The longer version:
It's performing three sequential casts.
reinterpret_cast<const volatile char&>
This is effectively casting to a char&
. The const
and volatile
only exist because _Tp
may be const
or volatile
, and reinterpret_cast
can add those, but would be unable to remove them.
const_cast<char&>
Now the const
and volatile
have been removed. const_cast
may do that.
reinterpret_cast<_Tp*>(&result)
Now the address is taken and the type is converted back to a pointer to the original type.
From inside out:
First it casts
__r
type to aconst volatile char&
: It's casting to achar&
just because it's a type that for sure doesn't have an overloadedoperator&
that does something funky. Theconst volatile
is there because those are restrictions, they can be added but not taken away withreinterpret_cast
._Tp
might've already beenconst
and/orvolatile
, in which case one or both were needed in this cast. If it didn't, the cast just added them needlessly, but it is written for the most restrictive cast.Next, to take away the
const volatile
you need aconst_cast
, which leads to the next part...const_cast<char&>
.From there they simply take the address and cast it to the type you want, a
_Tp*
. Note that_Tp
might beconst
and/orvolatile
, which mean those things could be added back at this point.