Consider the following program:
struct ghost
{
// ghosts like to pretend that they don't exist
ghost* operator&() const volatile { return 0; }
};
int main()
{
ghost clyde;
ghost* clydes_address = &clyde; // darn; that's not clyde's address :'(
}
How do I get clyde
's address?
I'm looking for a solution that will work equally well for all types of objects. A C++03 solution would be nice, but I'm interested in C++11 solutions too. If possible, let's avoid any implementation-specific behavior.
I am aware of C++11's std::addressof
function template, but am not interested in using it here: I'd like to understand how a Standard Library implementor might implement this function template.
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).
T&
to char const volatile&
const
and volatile
&
operator to take the addressT*
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.