2010-11-10 10 views
8

C++03 3.2.2... Se utiliza un objeto o función no sobrecargada si su nombre aparece en una expresión potencialmente evaluada. Una función miembro virtual se utiliza si no es puro ...¿Se utiliza una función de miembro virtual si no es pura?

Y luego más tarde en 3.2.3 tenemos: Cada programa deberá contener exactamente una definición de cada función o un objeto no en línea que se utiliza en ese programa; no se requiere diagnóstico. La definición puede aparecer explícitamente en el programa, se puede encontrar en la biblioteca estándar o definida por el usuario, o (cuando corresponda) está implícitamente definida (ver 12.1, 12.4 y 12.8). Se debe definir una función en línea en cada unidad de traducción en la que se utiliza.

A lo largo de las líneas que estoy leyendo: una función virtual pura no se utiliza. El ODR se aplica solo a las funciones que se utilizan. ¿Esto no implica que lo siguiente sería legal? Supongo que la respuesta es no, no, pero entonces no puedo entender por qué.

//x.h 
struct A 
{ 
    virtual void f() = 0; 
}; 

//y.cpp 
#include "x.h" 
void A::f() 
{ 
} 

//z.cpp 
#include "x.h" 
#include <iostream> 
void A::f() 
{ 
    std::cout << "Hello" << std::endl; 
} 

//main.cpp 
#include "x.h" 
struct B:A 
{ 
    virtual void f() 
    { 
     A::f(); 
    } 
}; 

int main() 
{ 
    A* p = new B; 
    p->f(); 
} 
+0

¿Este compila/vincula? Siento que habría un error de enlace debido a una implementación duplicada de la estructura A. –

+0

@Rolland: ¿qué quieres decir? Una definición de clase puede y debe aparecer en cada unidad de traducción en la que se usa la clase ... –

+0

Muy buena pregunta :) – icecrime

Respuesta

11

Las dos cláusulas no son mutuamente excluyentes. Que una función virtual se use si no es pura, no significa que lo contrario se cumple. Si una función virtual es pura, no significa que no se use necesariamente. Todavía se puede usar "si su nombre aparece en una expresión potencialmente evaluada", como en su ejemplo: A::f();.

+0

Erm ... Todavía no estoy convencido. El término utilizado se define a través de la noción de "expresión potencialmente evaluada" y existe una excepción para las funciones virtuales puras. Entonces, A :: f() es una expresión potencialmente evaluada como x-> f(); pero esto último no significa que se use la función ... –

+0

Veamos la cita de @Steve M: Una función virtual pura necesita definirse solo si se llama con, o como con (12.4), la sintaxis de id calificado (5.1). "Si hubieran agregado. En este caso, se dice que se usará la función virtual pura, todo estaría bien. Pero no lo han hecho :) –

+1

3.2p2 no tiene sentido a menos que se lea como si cumplir con alguno de los requisitos individuales hace un símbolo utilizado. Por ejemplo, una función de asignación es utilizada por una expresión nueva. Pero ciertamente también se usa si se llama explícitamente usando 'operator new'. – aschepler

0

[class.abstract]: "Una función virtual pura necesita definirse solo si se llama con, o como con (12.4), la sintaxis de id calificado (5.1)".

Su A::f se llama por B::f, por lo que debe haber una sola definición de A::f.

+0

Acepto que debe haber una definición para A :: f de acuerdo con su presupuesto. Pero no veo qué cláusula requiere que haya una ** ** definición ** –

+0

@Armen Tsirunyan: consulte mi respuesta para su aclaración – Chubsdad

3

Este código infringe ODR. A :: f está definido de forma múltiple. Por lo tanto, tiene UB.

múltiples definiciones a través de unidades de traducción sólo se permiten para el siguiente como por $ 3,2/5

No puede haber más de una definición de un tipo de clase (cláusula 9), tipo de enumeración (7.2), inline función con enlace externo (7.1.2), plantilla de clase (cláusula 14), la plantilla no estático función (14.5.5), miembro de datos estáticos de una plantilla de clase (14.5.1.3), función de miembro de una plantilla de clase (14.5.1.1) o plantilla s especialización para la que no se especifican algunos parámetros de plantilla (14.7, 14.5.4) en un programa siempre que cada definición aparezca en una unidad de traducción diferente, y siempre que las definiciones cumplan los siguientes requisitos .

+0

;-) genial para leer que –

1

Como @Charles Bailey señaló, su A::f se utiliza de hecho aunque sea puramente virtual. Pero eso está al lado del punto principal.

No es exacto que la regla de una definición no se aplica a las funciones que no se utilizan. Tenemos:

3.2p1 Ninguna unidad de traducción debe contener más de una definición de cualquier variable, función, tipo de clase, tipo de enumeración o plantilla.

3.2p3 Cada programa debe contener exactamente una definición de cada función u objeto no en línea que se utiliza en ese programa; no se requiere diagnóstico.

En conjunto, estos requisitos parecen implicar que una función usada debe tener exactamente una definición, y una función no utilizada (incluyendo una función virtual pura que nunca se llama explícitamente) puede no tener definición o una definición única. En cualquier caso, las definiciones múltiples para una función no en línea hacen que el programa esté mal formado.

Al menos, estoy bastante seguro de que esa es la intención. Pero puede estar en un hoyo en el fraseo, ya que una lectura muy literal no dice en ninguna parte que múltiples definiciones diferentes de la misma función no utilizada en diferentes unidades de traducción están mal formadas.

// x.cpp 
void f() {} 
void g() {} 

// y.cpp 
#include <iostream> 
void f() { 
    std::cout << "Huh" << std::endl; 
} 
void h() {} 

// z.cpp 
void g(); 
void h(); 
int main() { 
    g(); 
    h(); 
    return 0; 
} 
+0

Sin esperar, 3.2p1 dice "unidad de traducción" . Creo que de hecho puede haber un agujero de fraseo. – aschepler

+0

Pero todavía no entiendo por qué se usa A :: f ... –

+0

3.2/1 no es un agujero de fraseo. Se aplica a funciones en línea, clases y enumeraciones que pueden definirse una vez _per translation unit_, a diferencia de las funciones y objetos no en línea. –

1

Esto está relacionado, pero fuera de tema: desde las citas parece que hay un agujero en la Norma bien: también debería decir que se utiliza un destructor virtual puro, y, que debe ser definida; al menos si existen objetos de clase derivados que se destruyen o si se define un destructor de tal, ya que el destructor de clase derivado debe llamar al destructor base, implícitamente lo hace con la sintaxis qualified :: id. La definición de tales destructores suele ser trivial, pero no puede eludirse y no puede generarse.

+0

12.4p7: "Un destructor puede ser declarado' virtual' (10.3) o puro 'virtual' (10.4); si se crean en el programa objetos de esa clase o cualquier clase derivada, se definirá el destructor". (Mi nota: 'objetos' incluye subobjetos miembros). – aschepler

Cuestiones relacionadas