virtuales que puro puras polimorfismo objeto modificador herencia funciones clase abstracta c++ pure-virtual language-lawyer

que - polimorfismo puro c++



Es posible que las funciones virtuales puras no tengan una definición en línea. ¿Por qué? (5)

Las funciones virtuales puras son aquellas funciones miembro que son virtuales y tienen el especificador puro ( = 0; )

La cláusula 10.4 párrafo 2 de C ++ 03 nos dice qué es una clase abstracta y, como nota al margen, lo siguiente:

[Nota: una declaración de función no puede proporcionar un especificador puro y una definición-nota final] [Ejemplo:

struct C { virtual void f() = 0 { }; // ill-formed };

-Final ejemplo]

Para aquellos que no están muy familiarizados con el tema, tenga en cuenta que las funciones virtuales puras pueden tener definiciones, pero la cláusula mencionada anteriormente prohíbe que tales definiciones aparezcan en línea (léxicamente en su clase). (Para los usos de la definición de funciones virtuales puras, puede ver, por ejemplo, este GotW )

Ahora, para todos los demás tipos y tipos de funciones, está permitido proporcionar una definición en clase, y esta restricción parece a primera vista absolutamente artificial e inexplicable. Ahora que lo pienso, parece que sí en la segunda y siguientes miradas :) Pero creo que la restricción no estaría allí si no hubiera una razón específica para eso.

Mi pregunta es: ¿alguien sabe esas razones específicas? Las buenas suposiciones también son bienvenidas.

Notas:

  • MSVC permite que los PVF tengan definiciones en línea. Así que no te sorprendas :)
  • la palabra en inline en esta pregunta no se refiere a la palabra clave en línea . Se supone que significa léxicamente en su clase

Buenas intuiciones ... bueno, considerando la situación:

  • es legal declarar la función en línea y proporcionar un cuerpo explícitamente en línea (fuera de la clase), por lo que claramente no hay objeción a la única implicación práctica de ser declarado dentro de la clase.
  • No veo posibles ambigüedades o conflictos introducidos en la gramática, por lo que no hay ninguna razón lógica para la exclusión de las definiciones de funciones in situ .

Mi suposición: el uso de cuerpos para funciones virtuales puras se realizó después de = 0 | { ... } = 0 | { ... } gramática fue formulada, y la gramática simplemente no fue revisada. Vale la pena considerar que hay muchas propuestas de cambios / mejoras en el lenguaje, incluidas aquellas para que cosas como esta sean más lógicas y consistentes, pero el número que son recogidos por alguien y redactados como propuestas formales es mucho más pequeño, y el número de aquellos que el Comité tiene tiempo de considerar, y cree que los proveedores de compiladores estarán preparados para implementarlos, es mucho más pequeño de nuevo. Cosas como esta necesitan un campeón, y tal vez eres la primera persona en ver un problema. Para familiarizarse con este proceso, consulte http://www2.research.att.com/~bs/evol-issues.html .


Dejando a un lado los destructores, las implementaciones de funciones virtuales puras son algo extraño, porque nunca reciben llamadas de la manera natural. es decir, si tiene un puntero o referencia a su clase Base, el objeto subyacente siempre será Derivado que anula la función, y que siempre recibirá un llamado.

La única forma de llamar a la implementación es utilizando la sintaxis Base :: func () de una de las sobrecargas de la clase derivada.

En realidad, de alguna manera esto lo convierte en un mejor objetivo para la creación de líneas, ya que en el punto donde el compilador quiere invocarlo, siempre está claro a qué sobrecarga se está llamando.

Además, si se prohibieran las implementaciones para funciones virtuales puras, habría una solución obvia de alguna otra función no virtual (probablemente protegida) en la clase Base a la que podría llamar de manera regular desde su función derivada. Por supuesto, el alcance sería menos limitado en el sentido de que podría llamarlo desde cualquier función.

(Por cierto, estoy bajo la suposición de que Base::f() solo puede Derived::anyOtherFunc() con esta sintaxis de Derived::f() y no de Derived::anyOtherFunc() . ¿Estoy de acuerdo con esta suposición?).

Los destructores virtuales puros son una historia diferente, en cierto sentido. Se usa como una técnica simplemente para evitar que alguien cree una instancia de la clase derivada sin que existan funciones virtuales puras en otro lugar.

La respuesta a la pregunta real de "por qué" no está permitido es realmente solo porque el comité de normas así lo dijo, pero mi respuesta arroja algo de luz sobre lo que estamos tratando de lograr de todos modos.


En el hilo SO "¿Por qué la función virtual pura es inicializada por 0?" Jerry Coffin proporcionó esta cita de The Design & Evolution of C ++ , sección §13.2.3, de Bjarne Stroustrup, donde agregué un poco de énfasis de la parte que considero relevante :

Se eligió la sintaxis curious =0 sobre la alternativa obvia de introducir una nueva palabra clave pura o abstracta porque en ese momento no veía ninguna posibilidad de que se aceptara una nueva palabra clave. Si hubiera sugerido puro, el Release 2.0 se habría enviado sin clases abstractas. Dada la elección entre una sintaxis más agradable y clases abstractas, elegí clases abstractas. En lugar de arriesgarme a retrasarme e incurrir en ciertas peleas sobre pura, utilicé la convención tradicional C y C ++ de usar 0 para representar "no está allí". La sintaxis =0 coincide con mi opinión de que un cuerpo de función es el inicializador de una función y también con la vista (simplista, pero generalmente adecuada) del conjunto de funciones virtuales que se implementan como un vector de punteros de función. [...]

Entonces, al elegir la sintaxis Bjarne estaba pensando en un cuerpo de función como una especie de inicializador como parte del declarador, y =0 como una forma alternativa de inicializador, uno que indicaba "ningún cuerpo" (o en sus palabras, "no existe") )

Es razonable pensar que uno no puede indicar "no está allí" y tener un cuerpo, en esa imagen conceptual.

O, aún en esa imagen conceptual, tener dos inicializadores.

Ahora, eso es lo que dicen mis poderes telepáticos, google-foo y el razonamiento suave. Supongo que nadie ha estado Interesado lo suficiente ™ para formular una propuesta al comité sobre la eliminación de esta restricción puramente sintáctica y el seguimiento de todo el trabajo que eso conlleva. Por lo tanto, sigue siendo así.


Las buenas suposiciones son bienvenidas, ¿dices?

Creo que el = 0 en la declaración proviene de tener la implementación en mente. Lo más probable es que esta definición signifique que obtienes una entrada NULL en el vtbl de vtbl de la información de clase, la ubicación donde se almacenan las direcciones de tiempo de ejecución de las funciones miembro de una clase.

Pero en realidad, cuando se establece una definición de la función en su archivo *.cpp , se introduce un nombre en el archivo objeto para el vinculador: una dirección en el archivo *.o donde se encuentra una función específica.

El enlazador básico necesita saber más acerca de C ++. Solo puede vincularse, aunque lo haya declarado como = 0 .

Creo que leí que es posible lo que describes, aunque olvidé el comportamiento: -) ...


No deberías tener tanta fe en el comité de estandarización. No todo tiene una razón profunda para explicarlo. Algo es tan solo porque al principio nadie pensó lo contrario y después de que nadie pensó que cambiarlo es lo suficientemente importante (creo que es el caso aquí); para cosas lo suficientemente antiguas, incluso podría ser un artefacto de la primera implementación. Algunos son el resultado de la evolución: hubo una razón profunda a la vez, pero la razón fue eliminada y la decisión inicial no fue reconsiderada nuevamente (podría ser también el caso aquí, donde la decisión inicial fue porque cualquier definición del la función pura estaba prohibida). Algunos son el resultado de la negociación entre diferentes puntos de vista y el resultado carece de coherencia, pero esta falta se consideró necesaria para llegar a un consenso.