Update: in C++11, one may use std::addressof
instead of boost::addressof
.
Let us first copy the code from Boost, minus the compiler work around bits:
template<class T>
struct addr_impl_ref
{
T & v_;
inline addr_impl_ref( T & v ): v_( v ) {}
inline operator T& () const { return v_; }
private:
addr_impl_ref & operator=(const addr_impl_ref &);
};
template<class T>
struct addressof_impl
{
static inline T * f( T & v, long ) {
return reinterpret_cast<T*>(
&const_cast<char&>(reinterpret_cast<const volatile char &>(v)));
}
static inline T * f( T * v, int ) { return v; }
};
template<class T>
T * addressof( T & v ) {
return addressof_impl<T>::f( addr_impl_ref<T>( v ), 0 );
}
What happens if we pass a reference to function ?
Note: addressof
cannot be used with a pointer to function
In C++ if void func();
is declared, then func
is a reference to a function taking no argument and returning no result. This reference to a function can be trivially converted into a pointer to function -- from @Konstantin
: According to 13.3.3.2 both T &
and T *
are indistinguishable for functions. The 1st one is an Identity conversion and the 2nd one is Function-to-Pointer conversion both having "Exact Match" rank (13.3.3.1.1 table 9).
The reference to function pass through addr_impl_ref
, there is an ambiguity in the overload resolution for the choice of f
, which is solved thanks to the dummy argument 0
, which is an int
first and could be promoted to a long
(Integral Conversion).
Thus we simply returns the pointer.
What happens if we pass a type with a conversion operator ?
If the conversion operator yields a T*
then we have an ambiguity: for f(T&,long)
an Integral Promotion is required for the second argument while for f(T*,int)
the conversion operator is called on the first (thanks to @litb)
That's when addr_impl_ref
kicks in. The C++ Standard mandates that a conversion sequence may contain at most one user-defined conversion. By wrapping the type in addr_impl_ref
and forcing the use of a conversion sequence already, we "disable" any conversion operator that the type comes with.
Thus the f(T&,long)
overload is selected (and the Integral Promotion performed).
What happens for any other type ?
Thus the f(T&,long)
overload is selected, because there the type does not match the T*
parameter.
Note: from the remarks in the file regarding Borland compatibility, arrays do not decay to pointers, but are passed by reference.
What happens in this overload ?
We want to avoid applying operator&
to the type, as it may have been overloaded.
The Standard guarantees that reinterpret_cast
may be used for this work (see @Matteo Italia's answer: 5.2.10/10).
Boost adds some niceties with const
and volatile
qualifiers to avoid compiler warnings (and properly use a const_cast
to remove them).
- Cast
T&
to char const volatile&
- Strip the
const
and volatile
- Apply the
&
operator to take the address
- Cast back to a
T*
The const
/volatile
juggling is a bit of black magic, but it does simplify the work (rather than providing 4 overloads). Note that since T
is unqualified, if we pass a ghost const&
, then T*
is ghost const*
, thus the qualifiers have not really been lost.
EDIT: the pointer overload is used for pointer to functions, I amended the above explanation somewhat. I still do not understand why it is necessary though.
The following ideone output sums this up, somewhat.
:)
) – TuskCComPtr<>
andCComQIPtr<>
have an overloadedoperator&
– Weberoperator&
? – IkedaT **
andCComPtr<T> *
. – Weberoperator char&()
. – Antwanantwerpoperator &
should use aninterface ** OutPtr()
/interface ** InOutPtr()
instead, that would make it explicit in the call (with acceptable overhead) – Urmiastd::addressof
must be able to obtain the address of an object, even if the object is of a type that overloads arbitrary operators, including conversion operators and the unary&
. Further, the Standard Library containers must be instantiable and usable with those perverse types as well (this requirement is new in C++11; it was not present in C++98/03). – Antoine