2012-11-21 12 views
5

Questa è una specie di seguito alla domanda this.La risoluzione di sovraccarico si risolve in una funzione non ancora visibile

#include <iostream> 

struct type1 {}; 
struct type2 {}; 

void foo(type1 x) 
{ 
    std::cout << "foo(type1)" << std::endl; 
} 

template<typename T> 
void bar() { 
    foo(T()); 
} 

int main() 
{ 
    bar<type1>(); 
    bar<type2>(); 
    return 0; 
} 

void foo(type2 x) 
{ 
    std::cout << "foo(type2)" << std::endl; 
} 

Nel codice precedente foo(type2) non è visibile al momento di istanziazione di bar<type2> in main. Eppure il codice viene compilato e produce il seguente output:

foo(type1) 
foo(type2) 

Come fa il compilatore sa che foo(type2) è disponibile quando si crea un'istanza bar<type2> in main?

EDIT: Sto cercando di capire di più su come funziona la risoluzione di sovraccarico durante l'istanziazione del modello. Si consideri il seguente codice:

#include <iostream> 

struct type1 {}; 
struct type2 {}; 
struct type3 { 
    operator type2() { return type2(); } 
}; 

void foo(type1 x) 
{ 
    std::cout << "foo(type1)" << std::endl; 
} 

void foo(type2 x) 
{ 
    std::cout << "foo(type2)" << std::endl; 
} 

int main() 
{ 
    foo(type3()); 
    return 0; 
} 

void foo(type3 x) 
{ 
    std::cout << "foo(type3)" << std::endl; 
} 

L'uscita è

foo(type2) 

Anche se una maggiore corrispondenza foo(type3) è disponibile, la chiamata foo(type3()) risolve a foo(type2) perché quello era l'unico candidato che è stato analizzato dal compilatore fino a quel punto. Consideriamo ora il seguente codice:

#include <iostream> 

struct type1 {}; 
struct type2 {}; 
struct type3 { 
    operator type2() { return type2(); } 
}; 

void foo(type2 x) 
{ 
    std::cout << "foo(type2)" << std::endl; 
} 

template<typename T> 
void bar() { 
    foo(T()); 
} 

int main() 
{ 
    bar<type3>(); 
    return 0; 
} 

void foo(type3 x) 
{ 
    std::cout << "foo(type3)" << std::endl; 
} 

L'uscita è

foo(type3) 

Cioè, nel punto della chiamata bar<type3>(), anche se solo foo(type2) è visibile, il compilatore prende ancora foo(type3) che viene dopo, perché questa è una corrispondenza più stretta.

risposta

3

La risposta viene trovata tramite la ricerca del nome dipendente dall'argomento (ADL) (che viene anche menzionata nella domanda collegata). foo(T()); ha due ricerche. Prima al tempo di definizione del modello, tutte le funzioni definite nel punto di definizione sono incluse nel set di sovraccarico. Ciò significa che quando il compilatore vede foo(T()); all'interno di bar, aggiunge solovoid foo(type1 x) al set di sovraccarico. Tuttavia, è disponibile una ricerca secondo 10, chiamata ADL. Al momento dell'istanza del modello, ad esempio bar<type2>();, si cerca uno foo nello stesso spazio dei nomi dell'argomento fornito, che in questo caso è type2. Poiché type2 si trova nel namespace globale, cerca uno foo che prende uno type2 nello spazio dei nomi globale e lo trova e risolve la chiamata. Se stai cercando informazioni dallo standard, vedi 14.6.4.2 Candidate functions.

Provare quanto segue e vedere il codice fallire. Questo perché non riesce a trovare foo nello stesso spazio dei nomi di a::type1.

#include <iostream> 

namespace a 
{ 
    struct type1 {}; 
} 

template<typename T> 
void bar() { 
    foo(T()); 
} 

int main() 
{ 
    bar<a::type1>(); 
    return 0; 
} 

void foo(a::type1 x) 
{ 
    std::cout << "foo(a::type1)" << std::endl; 
} 
+0

Il tuo codice si compila bene con GCC 4.6. – keveman

+0

@keveman: citazione da [qui] (http://gcc.gnu.org/gcc-4.7/changes.html): 'G ++ ora implementa correttamente le regole di ricerca a due fasi in modo che un nome non qualificato utilizzato in un modello debba avere una dichiarazione appropriata trovata o nell'ambito nell'ambito del punto di definizione del modello o per la ricerca dipendente dall'argomento nel punto di istanziazione. implementato da gcc 4.7. –

+0

Ok. Non funziona con gcc 4.7. Grazie. Tuttavia, ciò che mi ostacola è il fatto che, nel mio esempio, 'foo (tipo3)' non è disponibile né in ambito al punto di definizione del modello né in quello di istanziazione. Ancora questo è quello che viene chiamato. – keveman

6

Qualsiasi simbolo lasciato senza definizione deve essere sostituito durante il processo di collegamento, poiché la funzione foo(type2) potrebbe essere stata fornita in un altro file.

Il compilatore indica se la funzione necessaria è stata definita dalla fine dell'intero processo, quando non è possibile applicare ulteriori sostituzioni.

Al fine di chiarire la comprensione, è necessario essere consapevoli dei passaggi necessari per la compilazione, per esempio, un programma C comune:

  • prima, si espande tutte le macro sul vostro codice;

  • quindi il codice viene convalidato in base alla sintassi della lingua, in modo che possa essere convertito in linguaggio assembly: il processo di compilazione stesso; durante questo passaggio, ogni simbolo trovato senza una definizione viene annotato in una tabella con le voci (symbol, definition), che deve essere completata in un secondo momento, consentendo al programma di essere costruito correttamente;

  • Successivamente, il codice compilato in assembly verrà convertito in linguaggio macchina, vale a dire, gli oggetti verranno creati;

  • Infine, è necessario collegare gli oggetti già eseguibili, al fine di risolvere eventuali dipendenze sulle definizioni dei simboli; questo ultimo passaggio controlla i tuoi oggetti per simboli indefiniti, aggiungendo definizioni da altri moduli o da librerie, quindi, completando il programma.

Se qualsiasi simbolo non è stato correttamente "legata" alla sua definizione, il compilatore segnala un errore nel programma - il classico undefined reference to....

Considerando il codice che hai postato, il processo verrebbe eseguito finché non raggiunge il compilatore. Il compilatore dovrebbe attraversare il codice, notare la definizione di type1, type2, foo(type1 x) e bar<T>().

struct type1 {}; 
struct type2 {}; 

Quando aveva raggiunto il principale, avrebbe trovato la richiesta di bar<type1>();, e avrebbe chiamato foo(type1()), che è già noto, e può essere utilizzato in modo corretto.

void foo(type1 x) { 
    std::cout << "foo(type1)" << std::endl; 
} 

template<typename T> 
void bar() { 
    foo(T()); 
} 

int main() { 

    bar<type1>(); 
    bar<type2>(); 
    return 0; 

} 

Una volta che aveva raggiunto la chiamata successiva, bar<type2>();, che avrebbe cercato di chiamare foo(type2()), ma nessuna tale definizione sarebbe disponibile per l'utilizzo, quindi sarebbe in relazione questo invito come un simbolo sconosciuto, che deve essere sostituito da una definizione nei processi successivi.

Dopo che il compilatore ha eseguito il main, raggiunge una nuova definizione, che è esattamente quella che manca definizione sulla "tabella di traduzione" in fase di creazione.

void foo(type2 x) { 
    std::cout << "foo(type2)" << std::endl; 
} 

Quindi, nella fase successiva, la compilation è in grado di sostituire il simbolo con il rispettivo definizione, e il programma viene compilato correttamente.

Saluti!

+0

Guardate da vicino, non c'è nemmeno una dichiarazione di 'foo (tipo2)' prima di 'main'. Quindi, come sa il compilatore al momento di istanziare la 'barra ', che 'foo (type2)' sarà finalmente visibile? – keveman

+0

Questo è esattamente ciò che ho indicato: ogni simbolo trovato dal compilatore, a cui non è disponibile una definizione, è elencato in una "tabella di traduzione", che deve essere completata entro la fine del processo di modifica del collegamento. Se tale tabella ha una chiave senza un valore noto, il compilatore si lamenterà e verrà visualizzato il classico riferimento "non definito a ..."; altrimenti, la sostituzione deve essere eseguita. – Rubens

+0

Grazie per la tua risposta dettagliata, ma temo che tu abbia preso una tangente irrilevante.La vera domanda qui riguarda la risoluzione del sovraccarico del tempo di compilazione e non il tempo di collegamento. Nella seconda fase della ricerca del nome in due fasi nell'istanza della 'bar ', il compilatore C++ apparentemente sta facendo un po 'di ADL per determinare che 'foo (type2)' è il giusto 'foo' anche se non ha ancora visto' foo (tipo2) '. Probabilmente mi manca qualcosa su come e quando avviene l'istanziazione. – keveman

Problemi correlati