Actualización: en C ++ 11, uno puede usar en std::addressof
lugar de boost::addressof
.
Copiemos primero el código de Boost, menos el trabajo del compilador alrededor de 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 );
}
¿Qué sucede si pasamos una referencia a la función ?
Nota: addressof
no se puede usar con un puntero para funcionar
En C ++ si void func();
se declara, entonces func
es una referencia a una función que no toma argumentos y no devuelve ningún resultado. Esta referencia a una función se puede convertir trivialmente en un puntero a la función, desde @Konstantin
: De acuerdo con 13.3.3.2 a ambos, T &
y no se T *
pueden distinguir para las funciones. La primera es una conversión de identidad y la segunda es la conversión de función a puntero, ambas con rango de "coincidencia exacta" (13.3.3.1.1 tabla 9).
La referencia a la función pasa addr_impl_ref
, hay una ambigüedad en la resolución de sobrecarga para la elección de f
, que se resuelve gracias al argumento ficticio 0
, que es el int
primero y podría promoverse a a long
(Conversión integral).
Por lo tanto, simplemente devolvemos el puntero.
¿Qué sucede si pasamos un tipo con un operador de conversión?
Si el operador de conversión produce un a, T*
entonces tenemos una ambigüedad: f(T&,long)
se requiere una promoción integral para el segundo argumento, mientras que para f(T*,int)
el operador de conversión se llama al primero (gracias a @litb)
Ahí es cuando addr_impl_ref
entra en acción. El estándar C ++ exige que una secuencia de conversión pueda contener como máximo una conversión definida por el usuario. Al envolver el tipo addr_impl_ref
y forzar el uso de una secuencia de conversión, "deshabilitamos" cualquier operador de conversión con el que viene el tipo.
Así f(T&,long)
se selecciona la sobrecarga (y se realiza la Promoción Integral).
¿Qué pasa con cualquier otro tipo?
Por lo tanto, f(T&,long)
se selecciona la sobrecarga, porque allí el tipo no coincide con el T*
parámetro.
Nota: a partir de las observaciones en el archivo con respecto a la compatibilidad de Borland, las matrices no se descomponen en punteros, sino que se pasan por referencia.
¿Qué pasa en esta sobrecarga?
Queremos evitar aplicar operator&
al tipo, ya que puede haber sido sobrecargado.
La Norma garantiza que reinterpret_cast
se puede utilizar para este trabajo (consulte la respuesta de @Matteo Italia: 5.2.10 / 10).
Boost agrega algunas sutilezas const
y volatile
calificadores para evitar advertencias del compilador (y usa adecuadamente a const_cast
para eliminarlos).
- Fundido
T&
achar const volatile&
- Pelar el
const
yvolatile
- Solicite al
&
operador que tome la dirección
- Echado de vuelta a un
T*
El const
/ volatile
malabarismo es un poco de magia negra, pero simplifica el trabajo (en lugar de proporcionar 4 sobrecargas). Tenga en cuenta que, dado que T
no está calificado, si pasamos un ghost const&
, entonces lo T*
es ghost const*
, por lo tanto, los calificadores no se han perdido realmente.
EDITAR: la sobrecarga del puntero se utiliza para el puntero a las funciones, modifiqué un poco la explicación anterior. Sin embargo, todavía no entiendo por qué es necesario .
La siguiente salida de ideone resume esto, de alguna manera.