Considere el siguiente programa:
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 :'(
}
¿Cómo obtengo clyde
la dirección?
Estoy buscando una solución que funcione igualmente bien para todo tipo de objetos. Una solución C ++ 03 sería buena, pero también estoy interesado en las soluciones C ++ 11. Si es posible, evitemos cualquier comportamiento específico de implementación.
Soy consciente de la std::addressof
plantilla de función de C ++ 11 , pero no estoy interesado en usarla aquí: me gustaría entender cómo un implementador de la Biblioteca estándar podría implementar esta plantilla de función.
c++
c++11
operator-overloading
memory-address
James McNellis
fuente
fuente
:)
)CComPtr<>
yCComQIPtr<>
tener un sobrecargadooperator&
Respuestas:
Actualización: en C ++ 11, uno puede usar en
std::addressof
lugar deboost::addressof
.Copiemos primero el código de Boost, menos el trabajo del compilador alrededor de bits:
Nota:
addressof
no se puede usar con un puntero para funcionarEn C ++ si
void func();
se declara, entoncesfunc
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 seT *
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 def
, que se resuelve gracias al argumento ficticio0
, que es elint
primero y podría promoverse a along
(Conversión integral).Por lo tanto, simplemente devolvemos el puntero.
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 paraf(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 tipoaddr_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).Por lo tanto,
f(T&,long)
se selecciona la sobrecarga, porque allí el tipo no coincide con elT*
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.
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
yvolatile
calificadores para evitar advertencias del compilador (y usa adecuadamente aconst_cast
para eliminarlos).T&
achar const volatile&
const
yvolatile
&
operador que tome la direcciónT*
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 queT
no está calificado, si pasamos unghost const&
, entonces loT*
esghost 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.
fuente
f
sobrecargas eran plantillas de funciones, mientras que son funciones miembro regulares de una clase de plantilla, gracias por señalarlo. (Ahora solo necesito averiguar cuál es el uso de la sobrecarga, ¿algún consejo?)char*
". Gracias MatthieuT*
? EDITAR: Ahora veo. Lo haría, pero con el0
argumento terminaría en una encrucijada , por lo que sería ambiguo.Uso
std::addressof
.Puedes pensar que hace lo siguiente detrás de escena:
Las implementaciones existentes (incluyendo Boost.Addressof) hacen exactamente eso, solo teniendo cuidado adicional
const
yvolatile
calificación.fuente
El truco detrás
boost::addressof
y la implementación proporcionada por @Luc Danton se basa en la magia dereinterpret_cast
; la norma establece explícitamente en §5.2.10 ¶10 queAhora, esto nos permite convertir una referencia de objeto arbitraria a a
char &
(con una calificación de cv si la referencia está calificada por cv), porque cualquier puntero se puede convertir a a (posiblemente calificado por cv)char *
. Ahora que tenemos unchar &
, la sobrecarga del operador en el objeto ya no es relevante, y podemos obtener la dirección con el&
operador incorporado .La implementación de impulso agrega algunos pasos para trabajar con objetos calificados para cv: el primero
reinterpret_cast
se realiza paraconst volatile char &
, de lo contrario, unchar &
molde simple no funcionaríaconst
y / ovolatile
referencias (reinterpret_cast
no se puede eliminarconst
). Luego,const
yvolatile
se elimina conconst_cast
, se toma la dirección&
y se realiza una finalreinterpet_cast
para el tipo "correcto".Se
const_cast
necesita para eliminar elconst
/volatile
que podría haberse agregado a referencias no constantes / volátiles, pero no "daña" lo que fue unconst
/volatile
referencia en primer lugar, porque el finalreinterpret_cast
volverá a agregar la calificación cv si era allí en primer lugar (reinterpret_cast
no puede eliminar elconst
pero puede agregarlo).En cuanto al resto del códigoaddressof.hpp
, parece que la mayor parte es para soluciones alternativas. Elstatic inline T * f( T * v, int )
parece ser necesario sólo para el compilador Borland, pero su presencia introduce la necesidad de queaddr_impl_ref
, de lo contrario los tipos de puntero serían capturados por esta segunda sobrecarga.Editar : las diversas sobrecargas tienen una función diferente, vea @Matthieu M. excelente respuesta .Bueno, ya no estoy seguro de esto tampoco; Debería investigar más ese código, pero ahora estoy cocinando la cena :), lo echaré un vistazo más tarde.
fuente
void func();
boost::addressof(func);
. Sin embargo, eliminar la sobrecarga no impide que gcc 4.3.4 compile el código y produzca la misma salida, por lo que todavía no entiendo por qué es necesario tener esta sobrecarga.He visto una implementación de
addressof
hacer esto:¡No me preguntes cómo se conforma esto!
fuente
char*
es la excepción enumerada para las reglas de alias de tipo.reinterpret_cast<char*>
bien definido.[unsigned] char *
y, por lo tanto, leer la representación de objeto del objeto señalado. Esta es otra área dondechar
tiene privilegios especiales.Eche un vistazo a boost :: addressof y su implementación.
fuente
addressof
devuelve el puntero en sí. Es discutible si es lo que el usuario quería o no, pero así es como se especifica.addr_impl_ref
, por lo que la sobrecarga del puntero nunca debe llamarse ...