2013-05-22 9 views
7

Credevo di aver capito ricerca del nome molto bene (dopo aver visto diversi video su di esso e leggere un sacco), ma ho appena colpito questo caso:Perché il compilatore non prende il nome dello spazio dei nomi quando quelli interni non funzionano?

#include <iostream> 

namespace test{ 

    struct Id 
    {}; 

    void do_something(const Id&){ std::cout << "Hello, World!" << std::endl; } 

    class Test 
    { 
    public: 

    void do_something() { std::cout << "WTF!" << std::endl; } 

    void run() 
    { 
     Id id; 
     do_something(id); // doesn't compile 
    } 

    }; 

} 

int main() 
{ 
    test::Test my_test; 
    my_test.run(); 
} 

La linea di punta non lo fanno compilare (su GCC4.8 e VC11U2) perché tenta di utilizzare la funzione membro test::Test::do_something() anziché lo spazio dei nomi test::do_something(const Id&) che sembra l'unico candidato possibile.

A quanto pare il nome della funzione membro nasconde i nomi con ambito spazio nomi che mi sorprende perché ricordo di aver usato codice simile in altri contesti senza questo problema di spawning (ma le condizioni potrebbero essere molto diverse alla fine).

La mia domanda è: questi compilatori confermano lo standard?

(ricerca del nome è molto difficile da capire leggendo il documento uniforme, purtroppo, quindi ho bisogno di conferme di esperti)

+10

No, no. La ricerca del nome è facile. È solo [una semplice piccola formula.] (Https://twitter.com/JamesMcNellis/status/336513318686695424) –

+0

'do_something' non è globale; è nello spazio dei nomi 'test'. – chepner

+0

@chepner Truem fammi risolvere questo problema. – Klaim

risposta

1

Il compilatore raccogliere tutte le "candidature", che sarà della stessa portata a meno ADL è coinvolto e quindi prova a scegliere la migliore corrispondenza se disponibile. In nessuna circostanza una corrispondenza fallita causerà il tentativo di trovare nomi candidati aggiuntivi da ambiti alternativi.

Questo è molto simile a come i compilatori eseguono prima la risoluzione del sovraccarico e poi THEN controllano il pubblico/privato del membro per vedere se è effettivamente accessibile.

g ++ ha una comoda opzione per cercare le ombre (non sono sicuro che lo avvertirebbe in modo specifico).

+0

[basic.lookup.unqual] "la ricerca del nome termina non appena viene trovata una dichiarazione per il nome." Raccoglie davvero tutti ** i nomi **? – dyp

+1

Non è coinvolto ADL qui? – Klaim

+0

@Klaim "Sia X il set di ricerca prodotto dalla ricerca non qualificata (3.4.1) e sia Y il set di ricerca prodotto dalla ricerca dipendente dall'argomento (definito come segue) .Se X contiene una dichiarazione di un membro della classe o [ ...] allora Y è vuoto. " basic.lookup.argdep]/3 – dyp

1

Nome di ricerca è molto difficile da capire leggendo il documento uniforme, purtroppo, quindi ho bisogno di conferme esperti

Io non sono un esperto con qualsiasi mezzo, ma ecco come ho capito le regole nome di ricerca dello standard.

Due esempi:

void foo(int); 

namespace associated 
{ 
    struct bee {}; 
    void flower(bee); 
} 

namespace bar 
{ 
    void foo(); 
    void flower(); 

    void test() 
    { 
     foo(42);     // (A) 
     flower(associated::bee()); // (B) 
    } 
} 

int main() 
{ 
    bar::test(); 
} 

(A) non compilare, a causa di [basic.lookup.unqual]: "ricerca del nome termina non appena una dichiarazione viene trovato il nome"

(B) compilare, a causa di ADL; associated è uno spazio dei nomi associato.

Tuttavia, c'è [basic.lookup.argdep]/3:

Sia X l'insieme di ricerca prodotta dalla ricerca qualificato (3.4.1) e sia y l'insieme ricerca prodotta da argomento di ricerca dipendente (definito come segue).Se X contiene

  • una dichiarazione di un membro della classe, o
  • una dichiarazione di funzione blocco ambito che non è una dichiarazione utilizzando, o
  • una dichiarazione che non è né una funzione o una funzione template

quindi Y è vuoto. Altrimenti Y è l'insieme di dichiarazioni trovate negli spazi dei nomi associati ai tipi di argomento come descritto di seguito. L'insieme di dichiarazioni trovati dalla ricerca del nome è l'unione di X e Y.

Il primo punto si applica nel tuo esempio. Pertanto, penso di sì, i compilatori che rifiutano il tuo esempio sono conformi allo standard.

+3

questo è il motivo per cui spesso troverai "using std :: swap;" nelle implementazioni delle funzioni di swap dei membri della classe (oltre a portare lo swap in scope per i tipi fondamentali come 'int' ofc). Impedisce la ricerca della funzione di scambio membri che impedisca il funzionamento di ADL. –

+0

@ JohannesSchaub-litb Esiste un modo per rendere visibile una dichiarazione di una funzione dichiarata in uno spazio dei nomi in cui lo spazio dei nomi dipende da un argomento del tipo di modello? – dyp

4

Più una lingua fa di tutto per trovare un'interpretazione valida per un costrutto, più è probabile che un errore di battitura o un altro errore comporti il ​​compilatore che trova un significato valido ma errato. Il compilatore presuppone che se foo è definito all'interno di un determinato ambito e il codice all'interno di tale ambito utilizza foo, il programmatore intende che il codice utilizzi lo foo definito nell'ambito. Se il programmatore cerca di fare qualcosa con foo che non è consentito dalla sua definizione ambito interno, le probabilità sono molto buone che una delle seguenti condizioni:

  • Il programmatore lo scopo di fare qualche altra operazione leggermente diverso, che avrebbe sono stati validi sul foo interiore. Non ci si può aspettare che un compilatore sappia cosa intende il programmatore a meno che il programmatore lo specifichi.
  • Il programmatore intendeva eseguire l'operazione indicata, non rendendosi conto che l'interno foo non può supportarlo e quindi dovrà trovare qualche altra operazione o sequenza di operazioni che l'interno foo può supportare. Di nuovo, non ci si può aspettare che un compilatore generi un buon codice a meno che il programmatore indichi come usare correttamente foo.
  • Il programmatore intendeva eseguire l'operazione in questione sul foo esterno, ma ha rifiutato di dirlo esplicitamente. Se il compilatore volesse intuire che questo è ciò che intendeva il programmatore, potrebbe generare un codice che si comporterebbe in questo modo.

Solo se l'intenzione del programmatore era # 3, un compilatore potrebbe generare un codice che si comporterebbe come previsto. È molto più probabile, tuttavia, che un programmatore abbia realmente inteso # 1 o # 2. Se il compilatore rifiuta di compilare il codice anche se si presuppone che il # 3 produca codice valido, allora verrà rilevato uno qualsiasi degli errori sopra riportati e potrà quindi essere corretto. Al contrario, se il compilatore assumeva il punto # 3 ogni volta che poteva, allora il programmatore intendeva davvero i problemi # 1 o # 2 che non si sarebbero manifestati fino a quando il codice non fosse stato eseguito e si fosse comportato in modo contrario alla progettazione.

BTW, se avessi i miei druthers, applicherei questo principio alla case-sensitive nei linguaggi .NET, proibendo non solo la scrittura di qualsiasi identificatore in una moda incoerente con la definizione (come è fatto da C# ma non da vb .net), ma l'uso di qualsiasi identificatore che differisce solo in upper/lower-casing da uno in un ambito interno. Per esempio:

class foo 
{ 
    int x; 
    void bar() 
    { 
    int X=2; 
    x=4; // **** 
    return X; 
    } 
} 

Dato il codice di cui sopra, C# Direi che la linea con gli asterischi era destinato a scrivere il campo; dato un codice simile, vb.net assumerebbe che era destinato a scrivere la variabile locale.Personalmente, non mi piacciono entrambe le ipotesi; il principio di "dici esattamente cosa intendi" mi suggerirebbe che un compilatore dovrebbe richiedere al programmatore di dire this.x=4; o X=4;, nessuno dei quali potrebbe essere letto come se avesse il significato sbagliato.

+0

+1, anche se non sono d'accordo con l'uso di 'this' ovunque. Questo è un bug che colpisce i principianti, non un bug che potrebbe causare il caos nelle applicazioni del mondo reale. Inoltre, il compilatore emette un avviso. –

+0

@ EDS: L'unica "extra" volta che richiederebbe "questo" sarebbe nello scenario in cui esiste una variabile locale con un nome che è identico tranne che per la parte superiore/inferiore. Per inciso, non mi piace la famosa convenzione C# in cui il campo di supporto privato per la proprietà 'Foo' è chiamato' foo'; Non vedo alcun vantaggio su "_foo' o' _Foo'. A mio avviso, la sottolineatura principale è più visivamente distintiva della seconda iniziale minuscola, ed è legale in non meno contesti come nomi di campi che differiscono dai nomi di proprietà solo nel caso. – supercat

+0

Ad 3) Penso che questo non sia sempre possibile, ad es. se un tipo di argomento di una funzione è un parametro di modello. – dyp

6

La mia domanda è: questi compilatori confermano lo standard?

Sì. Prima che la risoluzione del sovraccarico decida quali funzioni sono valide per il candidati (che include il controllo del numero di parametri), il compilatore deve prima cercare il nome, per trovare tutti i candidati, fattibili e non vitali. Nel tuo esempio, la ricerca del nome si interrompe dopo aver trovato il membro do_something() in modo che la risoluzione del sovraccarico non abbia mai la possibilità di decidere se lo spazio dei nomi-scope sia valido.

3.4.1 [basic.lookup.unqual]/1: "In tutti i casi elencati in 3.4.1, gli ambiti vengono ricercati in una dichiarazione nell'ordine elencato in ciascuna delle rispettive categorie; termina non appena viene trovata una dichiarazione per il nome. "

3.4.1 [basic.lookup.unqual] il paragrafo 8 elenca i contesti cercati per il nome e ha anche un esempio che risponde esattamente alla domanda. L'ambito di Test viene cercato prima dello spazio dei nomi che lo racchiude, e come dice il paragrafo 1 "la ricerca del nome termina non appena viene trovata una dichiarazione per il nome".

2

Questo non è un problema di ricerca. Il punto chiave è che la ricerca completa prima della risoluzione di sovraccarico. Quando il compilatore vede do_something esegue la ricerca per capire che cosa significa, trova che è una funzione, che a sua volta attiva ADL per trovare altri potenziali sovraccarichi. Quindi la ricerca termina e viene avviata la risoluzione di sovraccarico. Quando la risoluzione del sovraccarico non riesce.

Problemi correlati