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::addressofinstead ofboost::addressof.Let us first copy the code from Boost, minus the compiler work around bits:
Note:
addressofcannot be used with a pointer to functionIn C++ if
void func();is declared, thenfuncis 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 bothT &andT *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 off, which is solved thanks to the dummy argument0, which is anintfirst and could be promoted to along(Integral Conversion).Thus we simply returns the pointer.
If the conversion operator yields a
T*then we have an ambiguity: forf(T&,long)an Integral Promotion is required for the second argument while forf(T*,int)the conversion operator is called on the first (thanks to @litb)That’s when
addr_impl_refkicks in. The C++ Standard mandates that a conversion sequence may contain at most one user-defined conversion. By wrapping the type inaddr_impl_refand 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).Thus the
f(T&,long)overload is selected, because there the type does not match theT*parameter.Note: from the remarks in the file regarding Borland compatibility, arrays do not decay to pointers, but are passed by reference.
We want to avoid applying
operator&to the type, as it may have been overloaded.The Standard guarantees that
reinterpret_castmay be used for this work (see @Matteo Italia’s answer: 5.2.10/10).Boost adds some niceties with
constandvolatilequalifiers to avoid compiler warnings (and properly use aconst_castto remove them).T&tochar const volatile&constandvolatile&operator to take the addressT*The
const/volatilejuggling is a bit of black magic, but it does simplify the work (rather than providing 4 overloads). Note that sinceTis unqualified, if we pass aghost const&, thenT*isghost 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.