2009-07-24 22 views
10

Dato il seguente codice, perché è stata selezionata la funzione foo(T*)?Quali sono le regole per la scelta tra le funzioni del modello sovraccarico?

Se rimuovo (la foo(T*)) il codice viene compilato e lavora correttamente, ma v4.4.0 ++ G (e probabilmente altri compilatori così) genererà due foo() funzioni: uno per char [4] e uno per char [ 7].

#include <iostream> 
using namespace std; 

template< typename T > 
void foo(const T&) 
{ 
    cout << "foo(const T&)" << endl; 
} 

template< typename T > 
void foo(T*) 
{ 
    cout << "foo(T*)" << endl; 
} 

int main() 
{ 
    foo("bar"); 
    foo("foobar"); 
    return 0; 
} 
+1

Divertente, MSVC9 chiama la funzione "foo " nei simboli di debug, ma - apparentemente poiché riconosce che l'implementazione è identica - utilizza la stessa implementazione per entrambe le chiamate. - Interessante domanda, comunque. – peterchen

+0

AFAIK VC piega le istanze del modello che generano un codice identico. – sbi

+1

GCC (4.2.4) esegue sia le istanze sia le istanze fino a -O3, dove le elude completamente per queste implementazioni e la rende in linea. –

risposta

6

Formalmente, quando si confrontano le sequenze di conversione, le trasformazioni di lvalue vengono ignorate. Le conversioni sono raggruppati in diverse categorie, come la regolazione qualificazione (T* ->T const*), lvalue trasformazione (int[N] ->int*, void() ->void(*)()), e altri.

L'unica differenza tra i due candidati è una trasformazione lvalue. I letterali stringa sono array che convertono in puntatori. Il primo candidato accetta la matrice per riferimento e quindi non avrà bisogno di una trasformazione lvalue. Il secondo candidato richiede una trasformazione lvalue.

Quindi, se ci sono due candidati che entrambe le specializzazioni di modelli di funzione sono ugualmente valide guardando solo alle conversioni, la regola è che quella più specializzata viene scelta eseguendo l'ordinamento parziale dei due.

Mettiamo a confronto i due, cercando in loro firma della loro funzione di lista di parametri

void(T const&); 
void(T*); 

Se scegliamo un certo tipo unico Q per la prima lista dei parametri e cercare di abbinare contro la seconda lista di parametri, ci sono corrispondenti Q contro T*. Ciò fallirà, dal momento che Q non è un puntatore. Quindi, il secondo è almeno specializzato quanto il primo.

Se facciamo il contrario, corrispondiamo allo Q* rispetto allo T const&. Il riferimento viene eliminato e i qualificatori di livello superiore vengono ignorati e il rimanente T diventa Q*. Questa è una corrispondenza esatta ai fini dell'ordine parziale, e quindi la deduzione dell'elenco dei parametri trasformati del secondo rispetto al primo candidato ha esito positivo. Poiché l'altra direzione (rispetto alla seconda) non è riuscita, il secondo candidato è più specializzato rispetto al primo - e di conseguenza, la risoluzione di sovraccarico preferirà la seconda, se ci sarebbe altrimenti un'ambiguità.

At 13.3.3.2/3:

standard sequenza conversione S1 è in una sequenza di conversione di sequenza conversione standard S2 se [...]

  • S1 è una corretta sottosequenza di S2 (confrontando le sequenze di conversione in forma canonica definito da 13.3.3.1.1, escludendo qualsiasi trasformazione Ivalue; la sequenza conversione identità è considerata una sottosequenza di eventuali non sequenza -identity conversione) o, se non che [...]

Poi 13.3.3/1

  • lascia ICSi (F) denotare la sequenza di conversione implicita che converte l'argomento i-th nella lista al tipo del parametro i-esimo della funzione vitale F. 13.3.3.1 definisce le sequenze di conversione implicite e 13.3.3.2 definisce cosa significa che una sequenza di conversione implicita deve essere una sequenza di conversione migliore o una sequenza di conversione peggiore di un'altra.

Date queste definizioni, una funzione F1 vitale è definito come una funzione migliore di un'altra funzione F2 valida se per tutti gli argomenti i, ICSI (F1) non è una sequenza di conversione peggio ICSI (F2), e poi [...]

  • F1 e F2 sono specializzazioni funzione di modello, e il modello di funzione F1 se non è più specializzata rispetto al modello per F2 secondo le regole di ordinamento parziali descritto in 14.5.5.2, o che , [...]

Infine, ecco la tabella delle conversioni implicite che possono partecipare a una sequenza di conversione standard a 13.3.3.1.1/3.

Conversion sequences http://img259.imageshack.us/img259/851/convs.png

+0

Questo NON è un caso di preferenza non di modello, perché non esiste una funzione non modello da scegliere dal compilatore. Il fatto è che const char [] è molto più simile a T * di T & (perché const char [] e const char * sono equivalenti). –

+0

scusa ho trascurato il modello <...> clausola prima del secondo. corretto :) –

+0

La tua affermazione che la corrispondenza a 'T *' è più vicina è sbagliata, però :) –

-1

Causa "" è un carattere *, che si adatta perfettamente alla funzione foo (T *). Quando lo rimuovi, il compilatore proverà a farlo funzionare con foo (T &), che richiede di passare il riferimento al char array che contiene la stringa.

Il compilatore non può generare una funzione che riceva il riferimento al char, poiché si passa l'intero array, quindi deve dereferenziarlo.

+0

Anche sbagliato. Per favore non indovinare. '" "' è un 'char const [1]'. Inoltre non puoi 'passare il riferimento'. Le espressioni hanno sempre un tipo non di riferimento. –

+0

Non ho mai detto che non lo è (in realtà, è "", co è const [2]), quello che intendevo è che è preferibile selezionare la funzione T *, se esiste. Per quanto riguarda la frase 'passing ref', stavo cercando di dire che l'opzione meno preferita è T &, che è fondamentalmente un riferimento a T (in questo caso, riferimento a char [N]), sebbene la mia risposta non suonasse abbastanza corretto, il tuo diritto con quello. Correggimi se sbaglio (e dopo averlo fatto, mi limiterò a rimuovere il mio post, in quanto non è utile rispetto al tuo, che è molto più completo). –

0

Sulla base di regole di risoluzione di sovraccarico (Appendice B di C++ Templates: The Complete Guide ha una buona visione d'insieme), stringhe letterali (const char []) sono più vicini a T * di T &, perché il compilatore non fa alcuna distinzione tra char [] e char *, quindi T * è la corrispondenza più vicina (const T * sarebbe una corrispondenza esatta).

In effetti, se si potrebbe aggiungere:

template<typename T> 
void foo(const T[] a) 

(che non è possibile), il compilatore vi direbbe che questa funzione è una ridefinizione di:

template<typename T> 
void foo(const T* a) 
+0

'T *' non è affatto una corrispondenza ravvicinata. Prova 'void f (char const (&)[1]); void f (char const *); int main() {f (" ");}': Questa chiamata, che ricorda la situazione di sopra usando non-templates, è ambigua –

+0

@ litb: T * è una corrispondenza molto più stretta di T e in questo caso Sì. Se includi un SIZE specifico (& [N]), allora questo lo rende ambiguo, ma T * corrisponde a un array arbitrario molto più vicino di T & fa. –

+0

@Nick, mentre void foo (T x []) è un sinonimo di void foo (T * x), non significa che non sia possibile istanziare void foo (T &) con T come tipo di matrice.L'equivalente è void foo (ArrayBaseType (&) [ArraySize]) – AProgrammer

2

La risposta completa è abbastanza tecnico.

Innanzitutto, i valori letterali stringa hanno il tipo char const[N].

Quindi c'è una conversione implicita da char const[N] a char const*.

Quindi, entrambe le funzioni del modello corrispondono, una che utilizza l'associazione di riferimento, una che utilizza la conversione implicita. Quando sono soli, entrambe le funzioni del template sono in grado di gestire le chiamate, ma quando sono entrambe presenti, dobbiamo spiegare perché il secondo foo (istanziato con T = char const [N]) è una corrispondenza migliore della prima (istanziato con T = char).Se si guardano le regole overloading (come dato da litb), la scelta tra

void foo(char const (&x)[4)); 

e

void foo(char const* x); 

è ambiguo (le regole sono abbastanza complicato, ma è possibile controllare scrivendo funzioni non template con tali firme e vedere che il compilatore si lamenta). In questo caso, la scelta viene fatta al secondo perché quello è più specializzato (anche in questo caso le regole per questo ordine parziale sono complicate, ma in questo caso è possibile passare un char const[N] a un char const* ma non a char const*char const[N] allo stesso modo di void bar(char const*) è più specializzato di void bar(char*) perché è possibile passare un char* a un char const* ma non viceversa).

Problemi correlati