sencillos resueltos programacion poo polimorfismo orientada objetos libro herencia ejercicios ejemplos ejemplo codigo clases c++ casting polymorphism overloading operator-keyword

resueltos - Sobrecarga y polimorfismo del operador de fundición C++



programacion orientada a objetos c++ libro pdf (1)

Estoy desconcertado con este comportamiento de C ++:

struct A { virtual void print() const { printf("a/n"); } }; struct B : public A { virtual void print() const { printf("b/n"); } }; struct C { operator B() { return B(); } }; void print(const A& a) { a.print(); } int main() { C c; print(c); }

Entonces, el cuestionario es, ¿cuál es el resultado del programa - a o b? Bueno, la respuesta es a. ¿Pero por qué?


El problema aquí es un error / falla / agujero en el estándar C ++ 03, con diferentes compiladores tratando de solucionar el problema de diferentes maneras. (Este problema ya no existe en el estándar C ++ 11).

Las secciones 8.5.3 / 5 de ambas normas especifican cómo se inicializa una referencia. Aquí está la versión C ++ 03 (la lista de numeración es mía):

Una referencia al tipo cv1 T1 se inicializa mediante una expresión del tipo cv2 T2 siguiente manera:

  1. Si la expresión inicializadora

    1. es un lvalue (pero no es un campo de bits), y "cv1 T1" es compatible con referencia con "cv2 T2" o
    2. tiene un tipo de clase (es decir, T2 es un tipo de clase) y se puede convertir implícitamente a un lvalue de tipo cv3 T3 , donde cv1 T1 es compatible con referencia a cv3 T3

    luego, la referencia se vincula directamente a la expresión inicializadora lvalue en el primer caso, y la referencia se vincula al resultado lvalue de la conversión en el segundo caso.

  2. De lo contrario, la referencia será a un tipo de const no volátil (es decir, cv1 será const ).

  3. Si la expresión inicializadora es un valor r, con T2 un tipo de clase, y cv1 T1 es compatible con referencias con cv2 T2 , la referencia está vinculada de una de las siguientes maneras (la opción está definida por la implementación):

    1. La referencia está vinculada al objeto representado por el valor de r (ver 3.10) o a un subobjeto dentro de ese objeto.
    2. Se cv1 T2 un temporal de tipo cv1 T2 [sic] y se llama a un constructor para copiar todo el objeto rvalue en el temporal. La referencia está vinculada a lo temporal o a un subobjeto dentro de lo temporal.

    El constructor que se usaría para hacer la copia será llamable, ya sea que la copia se haga o no.

  4. De lo contrario, se creará y se inicializará un temporal del tipo cv1 T1 partir de la expresión de inicialización utilizando las reglas para una inicialización de copia no de referencia (8.5). La referencia queda entonces vinculada a lo temporal.

Hay tres tipos involucrados en la pregunta en cuestión:

  • El tipo de referencia a crear. Los estándares (ambas versiones) denotan este tipo como T1 . En este caso, es struct A
  • El tipo de la expresión inicializadora. Los estándares denotan este tipo como T2 . En este caso, la expresión inicializadora es la variable c , por lo que T2 es la struct C Tenga en cuenta que dado que la struct A no es compatible con la struct C referencia struct C , no es posible vincular directamente la referencia a c . Se necesita un intermedio.
  • El tipo del intermedio. Los estándares denotan este tipo como T3 . En este caso, esta es la struct B Tenga en cuenta que al aplicar el operador de conversión C::operator B() a c se convertirá el lvalue c en un rvalue.

Las inicializaciones por lo que etiqueté como 1.1 y 3 están fuera porque la struct A no es compatible con la struct C referencia. Se debe usar el operador de conversión C::operator B() . 1.2 está fuera Debido a que este operador de conversión devuelve un rvalor, esta regla sale. Todo lo que queda es la opción 4, cree un temporal de tipo cv1 T1 . El estricto cumplimiento de la versión 2003 de la norma obliga a la creación de dos temporarios para este problema, aunque solo uno será suficiente.

La versión 2011 del estándar soluciona el problema al reemplazar la opción 3 con

  • Si la expresión inicializadora

    • es un xvalue, class prvalue, array prvalue o function cv1 T1 y cv1 T1 es compatible con referencia con cv2 T2 , o
    • tiene un tipo de clase (es decir, T2 es un tipo de clase), donde T1 no está relacionado con la referencia a T2 , y se puede convertir implícitamente a un xvalue, class prvalue o function lvalue de tipo cv3 T3 , donde cv1 T1 es la referencia compatible con cv3 T3 ,

    luego, la referencia está vinculada al valor de la expresión de inicialización en el primer caso y al resultado de la conversión en el segundo caso (o, en cualquier caso, a un subobjeto de clase base apropiado). En el segundo caso, si la referencia es una referencia de valor y la segunda secuencia de conversión estándar de la secuencia de conversión definida por el usuario incluye una conversión de valor a valor de r, el programa está mal formado.

Parece que la familia de compiladores gcc eligió un estricto cumplimiento sobre la intención (evite crear temporarios innecesarios), mientras que los otros compiladores que imprimen "b" eligieron la intención / correcciones al estándar. Elegir el cumplimiento estricto no es necesariamente recomendable; hay otros errores / fallas en la versión 2003 del estándar (por ejemplo, std::set ) donde la familia gcc eligió la cordura por encima del cumplimiento estricto.