13

Ho recentemente creato questo codice di esempio per illustrare l'utilizzo della funzione del modello variadico C++ 11.Necessità delle funzioni del modello di dichiarazione anticipata

template <typename Head, typename... Tail> void foo (Head, Tail...); 
template <typename... Tail> void foo (int, Tail...); 
void foo() {} 

template <typename... Tail> 
void foo (int x, Tail... tail) 
{ 
    std :: cout << "int:" << x; 
    foo (tail...); 
} 

template <typename Head, typename... Tail> 
void foo (Head x, Tail... tail) 
{ 
    std :: cout << " ?:" << x; 
    foo (tail...); 
} 

foo (int (123), float (123)); // Prints "int:123 ?:123.0" 

Se le prime due linee che forward-dichiarano foo sono omessi allora questo stamperà int:123int:123 invece. Questo ha sorpreso un programmatore C++ esperto e competente.

Era convinto che le dichiarazioni anticipate non dovessero essere necessarie perché il corpo non verrà istanziato fino alla seconda fase di ricerca a due fasi. Pensa che il compilatore (gcc 4.6) ha un bug.

Credo che il compilatore è proprio perché i due foo sono different base template functions e la scelta del modello di base deve essere locked-in durante la prima fase altrimenti si potrebbe violare la regola una definizione istanziando foo prima di tutte le versioni di esso sono stati definiti e quindi di nuovo in seguito (considerare come il linker presuppone che le definizioni di funzioni del template ridondanti siano identiche, intercambiabili e scartate).

Quindi, chi ha ragione?


Il GotW sopra-linked ben spiega come e perché i modelli di funzione non parzialmente specializzati, ma l'esistenza di funzioni template variadic sembra aumentare la confusione - l'intuizione che foo<int,Tail...> dovrebbe essere una specializzazione parziale di foo<Head,Tail...> è più forte di quell'intuizione per le funzioni non-variadiche, almeno per me.

+0

FWIW, clang ++ produce lo stesso risultato di gcc. – Cubbi

+0

Non sono esattamente sicuro, ma sembra che quando viene chiamato il foo(), dovrebbe istanziare il foo (int, Tail ...) che poi prova foo (tail ...) e quando la forward forward non è lì , non vedrà il foo (Head, Tail ...) e può solo scegliere il foo (int, Tail ...) uno ... Puoi anche aggiungere una chiamata a bar (x); nel foo (int, Tail ...) e quindi dichiarare una barra() dopo tutte le funzioni foo() ... non verrà trovato anche – PlasmaHH

risposta

8

GCC (e Clang) hanno ragione. MSVC potrebbe sbagliare perché non implementa correttamente la ricerca.

C'è, sembra, un equivoco dal tuo collega. Le regole per look-up sono:

  • la funzione di modello di base è necessario essere dichiarata prima di essere chiamato da una definizione
  • la funzione di modello specializzato deve essere dichiarata prima di essere un'istanza

Note : tali regole si applicano alle funzioni libere, all'interno di una classe non è richiesta una dichiarazione anticipata

Si noti che, poiché una definizione funge anche da dichiarazione, non è necessaria, nel proprio esempio, inoltrare dichiarare la versione int.

Esempio corretto:

template <typename T> void foo(T);    // declare foo<T> 

template <typename T> void bar(T t) { foo(t); }// call foo<T> (dependent context) 

template <> void foo<int>(int);    // declare specialiaztion foo<int> 

void bar(int i) { foo(i); }     // instantiate foo<T> with int 
               // which is the specialization 

Se non c'è modello base disponibile, questo è un errore. Se la specializzazione non viene dichiarata prima dell'istanza, essa non verrà utilizzata e ciò potrebbe in seguito significare una violazione della regola ODR (se un'altra istanza utilizza la specializzazione).

dallo standard (C++ 0x FDIS):

14.6.4.2

1. Per una chiamata di funzione che dipende da un parametro di template, le funzioni candidati si trovano utilizzando le consuete regole di ricerca (3.4.1, 3.4.2, 3.4.3) eccetto che:

- Per la parte della ricerca che utilizza la ricerca del nome non qualificato (3.4.1) o la ricerca del nome qualificato (3.4.3) , solo dichiarazioni di funzioni da il contesto di definizione del modello viene trovato.

- Per la parte della ricerca che utilizza gli spazi dei nomi associati (3.4.2), vengono trovate solo le dichiarazioni di funzione trovate nel contesto di definizione del modello o nel contesto di creazione di modelli.

Se il nome della funzione è un ID non qualificato e la chiamata sarebbe mal formata o troverebbe una corrispondenza migliore se la ricerca all'interno dei namespace associati considerava tutte le dichiarazioni di funzione con collegamento esterno introdotte in quegli spazi dei nomi in tutte le unità di traduzione , non considerando solo quelle dichiarazioni trovate nella definizione del modello e nei contesti di istanziazione del modello, allora il programma ha un comportamento indefinito.

Si noti che i paragrafi indicati sono per le funzioni regolari.

+1

Non sono sicuro di come il tuo esempio sia pertinente alla domanda. Nella domanda non ci sono specializzazioni esplicite, solo modelli di funzione sovraccaricati. –

+0

@Charles: ho colto l'occasione per colpire due piccioni con una pietra. –

6

bifase ricerca troverà:

  • funzioni che sono visibili nel punto di definizione e
  • funzioni che possono essere trovati da ADL al punto di istanziazione.

non può essere trovato da ADL, quindi se non è visibile al punto di definizione non verrà trovato affatto.

In altre parole, GCC ha ragione.

Problemi correlati