2009-08-12 27 views
23

tuve el siguiente código, que era básicamente,¿Es este código válido de C++?

class foo { 
    public: 
    void method(); 
}; 

void foo::foo::method() { } 

me había agregado por error un foo extra :: delante de la definición de foo :: método. Este código se compiló sin previo aviso con g ++ (ver 4.2.3), pero se borró usando Visual Studio 2005. No tenía un espacio de nombre llamado foo.

¿Qué compilador es el correcto?

+0

Sólo me preguntaba, pero ¿Está utilizando foo y método en tu código real? ¿O solo estás aliasando algún código con el que estabas trabajando? – jkeys

+1

Copié ese código como está en un único proyecto de archivo y agregué una función principal vacía. Esto realmente compila bajo gcc 4.3.3. – Matt

+1

Puedo confirmar que esto realmente compila en g ++ sin advertencias (lo he probado en 3.4.5 mingw) sin espacios de nombres ni nada. Por otro lado MSVC2008 lo rechaza. Muy extraño. –

Respuesta

25

Si leo correctamente el estándar, g ++ es correcto y VS es incorrecto.

ISO-IEC 14882-2003 (E), §9.2 clases (pag.153): Una clase-nombre se inserta en el ámbito en el que se declara inmediatamente después se ve el nombre de la clase. El nombre de clase también se inserta en el alcance de la clase en sí; esto se conoce como el nombre de clase inyectado. A los efectos de la verificación de acceso, el nombre de la clase inyectada se trata como si fuera un nombre de miembro público.

Tras los comentarios de abajo, también es particularmente útil para retener el siguiente relativa a las normas actuales de búsqueda de nombres:

ISO-IEC 14882 hasta 2.003 (E), §3.4-3 búsqueda de nombres (pag. 29): El nombre de la clase inyectada de una clase (cláusula 9) también se considera miembro de esa clase a los fines del ocultamiento de nombres y la búsqueda.

Sería extraño si no lo fuera, dada la parte final del texto en 9.2. Pero como comentó litb, esto nos asegura que, de hecho, g ++ está haciendo una interpretación correcta del estándar. No quedan preguntas.

+1

Interesante, ¿explica esto el hecho de que el método foo :: foo :: foo :: también funcione? – MikeT

+2

No del todo, no. La descripción de eso creo que se puede encontrar 3.4.3.1 - Búsqueda de nombres calificados: El nombre de una clase o miembro de espacio de nombres se puede consultar después de aplicar el :: operador de resolución de ámbito (5.1) a un especificador de nombre anidado que nomina su clase o espacio de nombres. Durante la búsqueda de un nombre anterior al operador de resolución de ámbito, se ignoran los nombres de objeto, función y enumerador. Si el nombre no es un nombre de clase (cláusula 9) o namespace-name (7.3.1), el programa está mal formado –

+0

De especial atención, "Durante la búsqueda de un nombre que precede al operador :: scope resolution" , el objeto, la función y los nombres del enumerador se ignoran ". –

0

¿Hay un espacio de nombres foo en algún otro módulo que incluya (y usted simplemente lo ignoraba)? De lo contrario, no es correcto. No estoy seguro de por qué g ++ permitió esto.

+0

En realidad, g ++ lo permite. Acabo de probar. – Matt

+0

"No estoy seguro de por qué g ++ permitió esto". – jkeys

1

Comeau online acepta sin ningún hickups, por lo que es ya sea válida o el segundo fallo en Como He encontrado en casi diez años.

11

Krugar tiene la respuesta correcta aquí. El nombre que se encuentra cada vez es el nombre de la clase inyectada.

El siguiente es un ejemplo que muestra al menos una razón por la que el compilador añade el nombre de clase inyectada:

namespace NS 
{ 
    class B 
    { 
    // injected name B // #1 
    public: 
    void foo(); 
    }; 

    int i;    // #2 
} 

class B     // #3 
{ 
public: 
    void foo(); 
}; 


int i;     // #4 

class A :: NS::B 
{ 
public: 
    void bar() 
    { 
    ++i;   // Lookup for 'i' searches scope of 
        // 'A', then in base 'NS::B' and 
        // finally in '::'. Finds #4 

    B & b = *this; // Lookup for 'B' searches scope of 'A' 
        // then in base 'NS::B' and finds #1 
        // the injected name 'B'. 

    } 
}; 

Sin nombre inyectada las reglas de búsqueda actuales se llegará a alcanzar el ámbito que contiene 'A' y encontraría ':: B' y no 'NS :: B'. Por lo tanto, necesitaríamos usar "NS :: B" en todas partes en A cuando quisiéramos referirnos a la clase base.

Otro lugar que inyectan nombres acostumbrarse están con plantillas, donde dentro de la plantilla de clase, el nombre inyectado proporciona una correspondencia entre el nombre de la plantilla y el tipo:

template <typename T> 
class A 
{ 
// First injected name 'A<T>' 
// Additional injected name 'A' maps to 'A<T>' 

public: 
    void foo() 
    { 
    // '::A' here is the template name 
    // 'A' is the type 'A<T>' 
    // 'A<T>' is also the type 'A<T>' 
    } 
}; 
Cuestiones relacionadas