tipos programacion poo modo hijo herencia derivadas derivada clases clase c++ class gcc type-conversion abstract-class

c++ - programacion - (¿Error GCC?) Conversión implícita a una clase derivada



la herencia es un modo de (1)

He encontrado un problema con la conversión implícita en C ++. El siguiente es un ejemplo mínimo:

struct A { virtual void f()=0; // abstract }; struct Ad : A { virtual void f() {} // not abstract }; struct B { operator Ad () const { return Ad(); } }; void test(A const &lhs) {} int main() { B b; test(b); }

Lo que me gustaría que el compilador hiciera es: convertir b en una variable de tipo Ad (usando la conversión definida en B) y pasar el resultado a prueba . Sin embargo, el código anterior no se compila en GCC (con C ++ 11 habilitado), el resultado es No se puede asignar un objeto de tipo abstracto ''A'' .

Algunas cosas a tener en cuenta:

  1. Clang compila esto.
  2. Si haces A no-abstracto cambiando f()=0; a f() {} , el código funciona bien.
  3. El compilador encuentra el operador de conversión (como lo indica 2), pero no hace lo que me gustaría que hiciera.

(Todas las citas de N4140, C ++ 14 FD)

TL; DR: El código está bien formado, esto es (o fue) un error de GCC.

Las reglas para la inicialización de referencia están cubiertas en [dcl.init.ref] / 5. Primero te mostraré la viñeta que no lo cubre; si quieres omitirlo, dirígete directamente a la tercera cita.

De lo contrario, la referencia será una referencia lvalue a un tipo const non-volátil (es decir, cv1 será const ), o la referencia será una referencia rvalue.

  • Si la expresión del inicializador
    • es un valor x (pero no un campo de bits), prvalue de clase, prvalue de matriz o lvalue de función 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 puede convertirse en un valor x , prvalue de clase o valor de función de tipo " cv3 T3 ", donde " cv1 T1 " es de referencia compatible con " cv3 T3 " (ver 13.3.1.6) ,

entonces la referencia está vinculada al valor de la expresión de inicializador 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).

Y la compatibilidad de referencia se define en [dcl.init.ref] / 4 1 .
Ahora considere el 13.3.1.6 vinculado:

En las condiciones especificadas en 8.5.3, una referencia puede vincularse directamente a un valor pL o un prvalor de clase que es el resultado de aplicar una función de conversión a una expresión de inicializador. La resolución de sobrecarga se utiliza para seleccionar la función de conversión que se invocará. Suponiendo que " cv1 T " es el tipo subyacente de la referencia que se está inicializando, y " cv S " es el tipo de expresión del inicializador, con un tipo de clase S a, las funciones candidatas se seleccionan de la siguiente manera:

  • Se consideran las funciones de conversión de S y sus clases base. Esas funciones de conversión no explícitas que no están ocultas en S y producen el tipo "referencia lvalue a cv2 T2 " ( al inicializar una referencia lvalue o una referencia rvalue a la función) o " cv2 T2 " [..], donde " cv1 T " es compatible con la referencia (8.5.3) con " cv2 T2 ", son funciones candidatas. Para la inicialización directa, [..].

Como puede ver, su función de conversión no es un candidato después de este párrafo. Por lo tanto, la siguiente viñeta en [dcl.init] / 5 es aplicable:

De otra manera:

  • Si T1 es un tipo de clase, las conversiones definidas por el usuario se consideran utilizando las reglas para la inicialización de copia de un objeto del tipo " cv1 T1 " mediante la conversión definida por el usuario (8.5, 13.3.1.4); el programa está mal formado si la inicialización de copia de no referencia correspondiente no está bien formada. El resultado de la llamada a la función de conversión, como se describe para la inicialización de la copia sin referencia, se usa luego para inicializar directamente la referencia. El programa está mal formado si la inicialización directa no da como resultado un enlace directo o si implica una conversión definida por el usuario.

Tenga en cuenta que la frase " el programa está mal formado si la inicialización de copia de referencia no correspondiente se formaría incorrectamente " puede implicar que, como

B b; A a = b;

está mal formado, el programa está mal formado. Sin embargo, creo que esto es un defecto o vaguedad en la redacción, y no es la razón por la que GCC no acepta el código. Seguramente, la redacción solo apunta a la inicialización en sí misma, no al hecho de que se puede crear un objeto derivado de tipo T1 (también conocido como A ).
Finalmente 13.3.1.4 acepta nuestra función de conversión:

Suponiendo que " cv1 T " es el tipo de objeto que se está inicializando, con T un tipo de clase, las funciones candidatas se seleccionan de la siguiente manera:

  • Los constructores de conversión (12.3.1) de T son funciones candidatas.
  • Cuando el tipo de expresión del inicializador es un tipo de clase " cv S ", se consideran las funciones de conversión no explícitas de S y sus clases base. [..] Aquellos que no están ocultos dentro de S y producen un tipo cuya versión cv no calificada es del mismo tipo que T o es una clase derivada de los mismos son funciones candidatas.

Ahora la última pregunta es si en

A const& ref(Ad());

ref está vinculado directamente. Y es.
Por lo tanto, la referencia de parámetro original se vincula directamente, y no se debe crear ningún objeto derivado de la mayoría de tipo A
Lo que GCC supuestamente piensa es que un temporal de tipo A debe inicializarse y la referencia debe estar vinculada a ese temporal. O pedantemente sigue la redacción defectuosa anterior, que es muy poco probable.

1)

Los tipos dados " cv1 T1 " y " cv2 T2 ", " cv1 T1 " están relacionados con la referencia " cv2 T2 " si T1 es del mismo tipo que T2 , o T1 es una clase base de T2 . " Cv1 T1 " es compatible con la referencia " cv2 T2 " si T1 está relacionado con la referencia a T2 y cv1 es la misma calificación cv, o mayor calificación cv que, cv2 .