¿Cuándo es upcasting ilegal en C++?
class base (3)
Estoy bastante seguro de entender la diferencia general entre upcasting y downcasting, particularmente en C ++. Entiendo que no siempre podemos abatir porque el lanzamiento de un puntero de clase base a un puntero de clase derivado supondría que el objeto de clase base al que se apunta tiene todos los miembros de la clase derivada.
Al principio del semestre, mi profesor dijo a la clase que a veces es ilegal subir de nivel en C ++, pero parece que no me di cuenta de por qué en mis notas, y no recuerdo cuándo ocurre esto.
¿Cuándo es ilegal subir en C ++?
Hay dos casos en los que la conversión ascendente está mal formada en C ++ (diagnosticada en tiempo de compilación):
La clase base en cuestión no es accesible :
class base {}; class derived : base {}; int main() { derived x; base& y = x; // invalid because not accessible. // Solution: C-style cast (as static_cast without access-check) base& y1 = (base&)x; }
El subobjeto de clase base en cuestión no es inequívoco :
class base {}; struct A1 : base {}; struct A2 : base {}; struct derived : A1, A2 {}; int main() { derived x; base& y = x; // invalid because ambiguous. // Solution 1, scope resolution: base& y1 = static_cast<A1::base&>(x); base& y2 = static_cast<A2::base&>(x); // Solution 2, intermediate unambiguous steps: A1& a1 = x; A2& a2 = x; base& ya1 = a1; base& ya2 = a2; }
Si la clase base es ambigua (se hereda dos o más veces a través de diferentes rutas), entonces no se puede hacer un upcast en un solo paso.
Si la clase base es inaccesible, la única forma de aumentar es usar un molde de estilo C. Este es un caso especial de ese elenco, es el único que puede hacer el trabajo. Básicamente, se comporta como un static_cast
que no está limitado por la accesibilidad.
Standardese.
C ++ 11 §5.4 / 4:
" ... en [un elenco de C] realizando una
static_cast
en las siguientes situaciones, la conversión es válida incluso si la clase base es inaccesible:
- un puntero a un objeto del tipo de clase derivado o un valor lvalor o rvalue del tipo de clase derivado se puede convertir explícitamente a un puntero o referencia a un tipo de clase base inequívoco, respectivamente;
- un puntero al miembro del tipo de clase derivado se puede convertir explícitamente en un puntero al miembro de un tipo de clase base no virtual no ambiguo;
- un puntero a un objeto de un tipo de clase base no virtual inequívoco, un glvalue de un tipo de clase base no virtual inequívoco, o un puntero al miembro de un tipo de clase base no virtual no ambiguo se pueden convertir explícitamente en un puntero, un referencia, o un puntero a miembro de un tipo de clase derivado, respectivamente.
Ejemplo de ambigüedad:
struct Base {};
struct M1: Base {};
struct M2: Base {};
struct Derived: M1, M2 {};
auto main() -> int
{
Derived d;
//static_cast<Base&>( d ); //! Ambiguous
static_cast<Base&>( static_cast<M2&>( d ) ); // OK
}
Ejemplo de base inaccesible, con ajuste de dirección (normalmente) en el molde:
struct Base { int value; Base( int x ): value( x ) {} };
class Derived
: private Base
{
public:
virtual ~Derived() {} // Just to involve an address adjustment.
Derived(): Base( 42 ) {}
};
#include <iostream>
using namespace std;
auto main() -> int
{
Derived d;
Base& b = (Base&) d;
cout << "Derived at " << &d << ", base at " << &b << endl;
cout << b.value << endl;
};
Si por "ilegal" te refieres mal formado, entonces es ilegal si la clase base es inaccesible o ambigua.
Es inaccesible cuando, por ejemplo, la clase base es privada.
class A {}; class B : A {}; ... B b; A *pa = &b; // ERROR: base class is inaccessible
Tenga en cuenta que incluso en C ++ 11 un elenco de estilo C puede "romper" la protección de acceso y realizar un upcast formalmente correcto
A *pa = (A *) &b; // OK, not a `reinterpret_cast`, but a valid upcast
Este uso debe evitarse, por supuesto.
Es ambiguo si el tipo de origen contiene múltiples subobjetos base del tipo de destino (a través de herencia múltiple).
class A {}; class B : public A {}; class C : public A {}; class D : public B, public C {}; D d; A *pa = &d; // ERROR: base class is ambiguous
En tales casos, la revalorización se puede realizar al "caminar" explícitamente la ruta ascendente deseada con las transmisiones intermedias hasta el punto en que la base ya no sea ambigua.
B* pb = &d; A* pa = pb; // OK: points to ''D::B::A'' subobject