Actualización: en C ++ 11, uno puede usar en std::addressoflugar 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: addressofno se puede usar con un puntero para funcionar
En C ++ si void func();se declara, entonces funces 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 intprimero 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_refentra 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_refy 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_castse puede utilizar para este trabajo (consulte la respuesta de @Matteo Italia: 5.2.10 / 10).
Boost agrega algunas sutilezas consty volatilecalificadores para evitar advertencias del compilador (y usa adecuadamente a const_castpara eliminarlos).
- Fundido
T&achar const volatile&
- Pelar el
constyvolatile
- Solicite al
&operador que tome la dirección
- Echado de vuelta a un
T*
El const/ volatilemalabarismo es un poco de magia negra, pero simplifica el trabajo (en lugar de proporcionar 4 sobrecargas). Tenga en cuenta que, dado que Tno 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.