2009-08-12 17 views
23

ho avuto il seguente codice, che era fondamentalmente,È questo codice C++ valido?

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

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

mi aveva accidentalmente aggiunto un extra :: foo di fronte alla definizione di foo :: metodo. Questo codice è stato compilato senza preavviso usando g ++ (ver 4.2.3), ma è stato errato utilizzando Visual Studio 2005. Non avevo uno spazio dei nomi chiamato foo.

Quale compilatore è corretto?

+0

Chiedo solo, ma stai usando foo e metodo nel tuo codice attuale? O stai solo facendo un aliasing con il codice con cui stai lavorando? – jkeys

+1

Ho copiato quel codice come è in un progetto di singolo file e ho aggiunto una funzione principale vuota. Questo in realtà compila sotto gcc 4.3.3. – Matt

+1

Posso confermare che questo effettivamente compila su g ++ senza avvisi (l'ho provato su 3.4.5 mingw) senza spazi dei nomi o altro. D'altra parte MSVC2008 lo rifiuta. Molto strano. –

risposta

25

Se leggo correttamente lo standard, g ++ ha ragione e VS è sbagliato.

ISO-IEC 14.882-2003 (E), §9.2 Classes (pag.153): A-nome di classe viene inserita nel campo di applicazione in cui è dichiarato immediatamente dopo il nome classe è visto. Il nome della classe viene anche inserito nell'ambito della classe stessa; questo è noto come nome della classe iniettata. Ai fini del controllo dell'accesso, il nome della classe immessa viene trattato come se fosse un nome membro pubblico.

Seguendo sui commenti qui sotto, è anche particolarmente utile per mantenere il seguente relativo alle regole attuali nomi di ricerca:

ISO-IEC 14.882-2003 (E), §3.4-3 Nome Lookup (pag. 29): Anche il nome di classe immesso di una classe (clausola 9) è considerato un membro di quella classe ai fini dell'identificazione e della ricerca dei nomi.

Sarebbe strano se non lo fosse, data la parte finale del testo in 9.2. Ma come commentato, questo ci rassicura che in effetti g ++ sta interpretando correttamente lo standard. Non ci sono domande

+1

Interessante, questo spiega il fatto che anche foo :: foo :: foo :: method funzioni? – MikeT

+2

Non interamente, no. 3.4.3.1 - Ricerca per nome qualificato: Il nome di una classe o di un membro dello spazio dei nomi può essere definito dopo l'operatore di risoluzione :: scope (5.1) applicato a uno specificatore del nome nidificato che nomina la sua classe o spazio dei nomi. Durante la ricerca di un nome precedente all'operatore di risoluzione :: scope, i nomi di oggetto, funzione e enumeratore vengono ignorati. Se il nome trovato non è un nome di classe (clausola 9) o nome spazio dei nomi (7.3.1), il programma è malformato –

+0

Di speciale nota, "Durante la ricerca di un nome che precede l'operatore di risoluzione :: scope , oggetto, funzione e nomi degli enumeratori vengono ignorati. " –

0

C'è uno spazio dei nomi in qualche altro modulo che includi (e ne eri semplicemente ignorante)? Altrimenti, non è corretto. Non sono sicuro del perché g ++ abbia permesso questo.

+0

In realtà, g ++ lo consente. Ho appena provato. – Matt

+0

"Non sono sicuro del perché g ++ abbia permesso questo." – jkeys

1

Comeau online accetta senza alcun hickups, quindi è valido o il secondo bug in como che ho trovato in quasi dieci anni.

11

Krugar ha la risposta corretta qui. Il nome che viene trovato ogni volta è il nome della classe iniettata.

Il seguente è un esempio che mostra almeno una ragione per cui il compilatore aggiunge il nome della classe iniettata:

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'. 

    } 
}; 

Senza il nome iniettato regole di ricerca attuali raggiungerebbero alla fine l'ambito di inclusione di 'A' e trovere ':: B' e non 'NS :: B'. Dovremmo quindi utilizzare "NS :: B" ovunque in A quando volevamo fare riferimento alla classe base.

Un altro luogo che hanno iniettato nomi abituarsi sono con i modelli, in cui all'interno del modello di classe, il nome iniettato fornisce una mappatura tra il nome del modello e il 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>' 
    } 
};