2010-11-28 14 views
13

Acabo de encontrar los siguientes párrafos en el borrador estándar de C++ 03 relevantes para la conversión de puntero a miembro.Conversión de puntero a miembro

4,11/2 Puntero a conversiones miembro

Un valor p de tipo “puntero a miembro de B de tipo cv T”, donde B es un tipo de clase, se puede convertir en un valor p de tipo "Puntero al miembro de D de tipo cv T", donde D es una clase derivada (cláusula 10) de B. Si B es una clase base inaccesible (cláusula 11), ambigua (10.2) o virtual (10.1) de D, una el programa que necesita esta conversión está mal formado. El resultado de la conversión hace referencia al mismo miembro que el puntero al miembro antes de que tuviera lugar la conversión, pero se refiere al miembro de la clase base como si fuera miembro de la clase derivada. El resultado se refiere al miembro en la instancia D de B. Dado que el resultado tiene el tipo "puntero a miembro de D de tipo cv T", puede desreferenciarse con un objeto D. El resultado es el mismo que si el puntero al miembro de B se elimina la referencia con el B sub-objeto de D. El valor de puntero nulo miembro se convierte en el valor del puntero miembro nula de la type.52 destino)

5.2.9/9 static_cast

un valor p de tipo “puntero a miembro de D de tipo CV1 T” puede ser convertido a un valor p de tipo “puntero a miembro de B de tipo cv2 T”, donde B es una clase base (cláusula 10) de D, si existe una conversión estándar válida de "apuntador a miembro de B de tipo T" a "apuntador a miembro de D de tipo T" (4.11), y cv2 es el mismo CV -calificación como, o mayor calificación cv que, cv1.63) El valor del puntero del miembro nulo (4.11) se convierte al valor del puntero del miembro nulo del tipo de destino. Si la clase B contiene el miembro original, o es una clase base o derivada de la clase que contiene el miembro original, el puntero resultante al miembro apunta al miembro original. De lo contrario, el resultado del lanzamiento no está definido. [Nota: aunque la clase B necesita no contiene el miembro original, el tipo dinámico del objeto sobre el que se quita la referencia al miembro debe contener el miembro original; ver 5.5. ]

Así que aquí está mi pregunta. Como dice 5.2.9/9, un puntero al miembro de D se puede convertir en un puntero al miembro de B, si existe una conversión válida descrita en 4.11/2. ¿Significa esto que si hay un miembro 'm' de D que no se hereda de B, el puntero al miembro 'm' no se puede convertir al tipo de puntero al miembro de B?

class Base { }; 
class Derived : public Base 
{ 
    int a; 
}; 
typedef int Base::* BaseMemPtr; 
BaseMemPtr pa = static_cast<BaseMemPtr>(&Derived::a); // invalid, as per 5.2.9/9 ? 

En la nota de 5.2.9/9, también dice que a pesar de la clase B no tiene por qué contener el miembro original, el tipo dinámico del objeto sobre el que se eliminan las referencias del puntero a miembro debe contener el miembro original .

Me confunden con la redacción del párrafo. ¿Es válido el código anterior?

He buscado en el sitio, y hay una pregunta similar, c++ inheritance and member function pointers, cuya respuesta solo cubría el caso de la conversión de puntero a miembro de la clase base a puntero a miembro de la clase derivada.

+0

Está asignando un puntero al valor del miembro de datos a un puntero a la variable de función del miembro. De lo contrario, sí, "el resultado del lanzamiento no está definido". – Potatoswatter

+0

Gracias, lo arreglé. – ashen

Respuesta

10

El código que escribió es perfectamente válido. No tiene nada de malo (aparte del hecho de que Derived::a es privado). Está bien formado y el comportamiento está definido (hasta ahora). Como dice la parte citada de la norma, es perfectamente legal subir los indicadores de miembro utilizando un static_cast explícito, que es exactamente lo que está haciendo. 5.2.9/9 nunca dice que el miembro puntiagudo tiene que estar presente en la clase base.

También, como usted correctamente citado de la norma, se requiere la presencia del miembro real en el objeto más tarde en el momento de eliminar la referencia del puntero, no en el momento de la inicialización. Esto, por supuesto, depende del tipo dinámico del objeto utilizado en el lado izquierdo del operador de acceso de miembro (->* o .*). El tipo solo se conoce en tiempo de ejecución y, por lo tanto, no puede ser verificado por el compilador.

Este requisito se incluye como una mera nota en 5.2.9/9, pero se reitera en una forma más formal de 5,5/4

4 Si el tipo dinámico del objeto no contiene el miembro al que se refiere el puntero , el comportamiento es undefined.

Así, por ejemplo, en el contexto de su ejemplo, las siguientes líneas de código están bien formadas

Base b; 
b.*pa; // 1 

Derived d; 
d.*pa; // 2 

Base *pb = &d; 
pb->*pa; // 3 

Sin embargo, el primer desreferenciar produce un comportamiento no definido (ya objeto b no contiene el miembro), mientras que tanto el segundo como el tercero son perfectamente legales.

+0

Excelente respuesta. Pero no puedo evitar mencionar que 'sizeof b. * Pa' da un comportamiento definido, a pesar de" eliminar referencias "de un miembro inexistente. (Porque nada en C++ es simple ... :-P) –

+1

@j_random_hacker: Bueno, sí. En mi caso, la ocurrencia de un comportamiento indefinido está ligada al proceso de desreferencia del puntero durante la * evaluación * de la expresión, mientras que en el caso de 'sizeof' el argumento nunca se evalúa. – AnT

+0

VC emite una advertencia de C4407 cuando modifico estáticamente un puntero para que funcione miembro de la clase derivada al segundo tipo de base, por lo tanto, de acuerdo con el estándar y su respuesta, no cumple con los estándares, ¿verdad? – ashen

Cuestiones relacionadas