c++ - Descenso dinámico en la herencia privada dentro del ámbito privado
casting private (2)
5.2.7 (ISO / IEC 14882, 12/29/2003) es bastante explícito en este punto:
[sobre la expresión
dynamic_cast<T>(v)
]Si
T
es "puntero a cv1B
" yv
tiene el tipo "puntero a cv2D
" de manera queB
es una clase base deD
, el resultado es un puntero al subobjetoB
único del objetoD
apuntado porv
. [... bla bla sobre cv1 y cv2 ...] y B será una clase de base accesible e inequívoca de D (énfasis mío)
(recuérdese 11.2 "Se dice que una clase base es accesible si un miembro público inventado de la clase base es accesible." ).
Esto explica por qué el primer reparto funciona. Ahora, para el segundo:
[...]
De lo contrario, se aplica una verificación de tiempo de ejecución para ver si el objeto señalado o al que hace referencia
v
puede convertirse al tipo apuntado o referido porT
La verificación en tiempo de ejecución se ejecuta lógicamente de la siguiente manera:
- Si, en el objeto más derivado apuntado (referido) por
v
,v
apunta (se refiere) a un subobjetopublic
clase base de un objetoT
, y si solo un objeto de tipo T se deriva del subobjetivo señalado (referido) Parav
, el resultado es un puntero (un valor de l) que se refiere a ese objetoT
- De lo contrario, si
v
apunta (se refiere) a un subobjetopublic
clase base del objeto más derivado, y el tipo del objeto más derivado tiene una clase base, de tipoT
, que es inequívoca ypublic
, el resultado es un puntero (un lvalor que se refiere) al subobjetoT
del objeto más derivado.- De lo contrario, la verificación en tiempo de ejecución falla.
El valor de un tipo fallido de conversión a puntero es el valor de puntero nulo del tipo de resultado requerido. Una conversión fallida al tipo de referencia lanza bad_cast (18.5.2).
Por lo tanto, parece que el comportamiento que observa se debe a la herencia private
: incluso si la clase base es accesible, no es pública , y el estándar requiere que sea público , no accesible.
Molesto, ¿no es así? No tengo el borrador de C ++ 0x a mano, tal vez alguien pueda editar mi respuesta con citas, en caso de que las cosas hayan cambiado.
¿Hay otra manera de lograr este reparto?
Depende de lo que quieras hacer. Básicamente, la herencia privada es solo otro dispositivo para realizar la composición. Si realmente debe devolver un puntero a la instancia derivada privada, haga pública la herencia o devuelva un miembro.
De todos modos, te alegrará saber que static_cast
no parece tener esta limitación:
5.2.9. [sobre
static_cast<T>(v)
] [...]Un valor de tipo "puntero a cv1 B", donde B es un tipo de clase, se puede convertir a un valor de tipo "puntero a cv2 D", donde D es una clase derivada (cláusula 10) de B, si es un estándar válido la conversión de "puntero a D" a "puntero a B" existe (4.10), cv2 es la misma calificación cv que, o mayor calificación cv que, cv1, y B no es una clase base virtual de D. El puntero nulo el valor (4.10) se convierte al valor de puntero nulo del tipo de destino. Si el valor de tipo "puntero a cv1 B" apunta a una B que es en realidad un subobjeto de un objeto de tipo D, el puntero resultante apunta al objeto que encierra de tipo D. De lo contrario, el resultado de la conversión no está definido .
por lo que si está seguro de cuál es el tipo dinámico real del puntero, se le permite a static_cast
dentro de foo
.
Me interesaría cualquier información adicional acerca de por qué existe esta inconsistencia.
Un pellizco en esta pregunta que me he encontrado. Considerar:
class A {};
class B : private A {
static void foo();
};
void B::foo(){
B* bPtr1 = new B;
A* aPtr1 = dynamic_cast<A*>(bPtr1); // gives pointer
B* bPtr2 = dynamic_cast<B*>(aPtr1); // gives NULL
}
Como aPtr1
es, de hecho, del tipo B*
, y como tenemos acceso total a B
y su herencia de A
, esperaba que ambas versiones funcionen. Pero no lo hacen; ¿por qué? ¿Hay otra manera de lograr este reparto?
Tenga en cuenta que:
- Si
foo()
no fuera miembro de B, ambos lanzamientos fallarían. - Si
B
hereda deA
públicamente, ambos lanzamientos funcionarán.
No funcionan porque no hay funciones virtuales en A. Cuando haces el downcast, eso es trivial: el compilador probablemente ni siquiera se moleste en hacer una comprobación. Cuando haces un upcast, el compilador debe verificarlo, pero solo está definido para funcionar cuando tienes funciones virtuales. Cuando no lo haga, entonces el compilador no realizará la comprobación y el resultado será NULL
.
El nivel de protección de herencia y otros problemas de accesibilidad son ortogonales al problema, y solo existen en tiempo de compilación, si el programa se compila, entonces funcionan bien.
Tenga en cuenta que:
Si foo () no fuera miembro de B, ambos lanzamientos fallarían.
Si B hereda de A públicamente, ambos lanzamientos funcionarán.
Eso no es cierto. foo()
tiene absolutamente ninguna relación con la funcionalidad RTTI, no es virtual y ni siquiera es un miembro de instancia. Si B hereda de A públicamente, A aún no tiene funciones virtuales y no funcionará.