2012-12-20 3 views
12

programma minimo:Sovraccarico di funzioni modello con firme identiche, perché funziona?

#include <stdio.h> 

#include <type_traits> 

template<typename S, typename T> 
int foo(typename T::type s) { 
    return 1; 
} 

template<typename S, typename T> 
int foo(S s) { 
    return 2; 
} 

int main(int argc, char* argv[]) { 
    int x = 3; 
    printf("%d\n", foo<int, std::enable_if<true, int>>(x)); 

    return 0; 
} 

uscita:

1 

Perché non questo invia un errore di compilazione? Quando viene generato il codice modello, le funzioni int foo(typename T::type search) e int foo(S& search) non hanno la stessa firma?

Se si cambia la funzione di modello di firme un po ', funziona ancora (come ci si aspetta dato l'esempio di cui sopra):

template<typename S, typename T> 
void foo(typename T::type s) { 
    printf("a\n"); 
} 

template<typename S, typename T> 
void foo(S s) { 
    printf("b\n"); 
} 

Tuttavia questo non e tuttavia l'unica differenza è che uno ha una firma int e l'altra è definita dal primo parametro template.

template<typename S, typename T> 
void foo(typename T::type s) { 
    printf("a\n"); 
} 

template<typename S, typename T> 
void foo(int s) { 
    printf("b\n"); 
} 

errore del compilatore (Clang):

test.cpp:26:2: error: call to 'foo' is ambiguous 
foo<std::enable_if<true, int>>(3); 
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 
test.cpp:16:6: note: candidate function [with T = std::__1::enable_if<true, int>] 
void foo(typename T::type s) { 
     ^
test.cpp:21:6: note: candidate function [with T = std::__1::enable_if<true, int>] 
void foo(int s) { 
     ^
1 error generated. 

Sto utilizzando il codice simile a questo per un progetto su cui sto lavorando e ho paura che ci sia un modo sottile per il linguaggio che ho Non capisco che causerà qualche comportamento indefinito in alcuni casi. Dovrei anche menzionare che si compila sia su Clang che su VS11, quindi non penso che sia solo un bug del compilatore.


Edit: corretto secondo caso (errore di battitura); aggiunto un messaggio di errore da Clang.

Modifica n. 2: per quelli di voi che hanno chiesto cosa significa T :: type.

Da http://en.cppreference.com/w/cpp/types/enable_if:

modello < bool B, classe T = vuoto> struct enable_if;

Se B è vero, std :: enable_if ha un tipo typedef di membro pubblico, uguale a a T; altrimenti, non vi è alcun membro typedef.

enable_if è una struttura. Fondamentalmente, se l'espressione valutata nel primo parametro template di enable_if è vera (e nel caso dei miei esempi sopra, lo è), allora ci sarà un membro pubblico type che ha lo stesso tipo del secondo parametro template.

Nel caso di enable_if<true, int>, enable_if :: type ha un tipo di int.

+1

Non sorprende che il tuo ultimo caso non venga compilato. Stai utilizzando due argomenti del modello quando la funzione (s) ne riceve uno solo. –

+0

Grazie, credo di aver copiato il caso sbagliato lì. Fisso. – vmrob

+0

Sono un noob in C++ quindi non disturbarmi ... Il primo esempio non funziona perché stai ignorando il primo metodo? Sono interessato a conoscere la risposta a questo. Ho intenzione di imparare il C++ l'anno prossimo. –

risposta

7

La prima funzione è considerata più specializzata della prima.

La funzione

int foo(typename T::type) 

potrebbe corrispondere

template <typename S,typename T> int foo(S s) 

utilizzando T :: tipo del valore del parametro S, ma

int foo(S s) 

non corrisponderanno

template <typename S,typename T> int foo(typename T::type) 

perché T non può essere dedotto.

La logica è descritta nello standard C++ 03 nella sezione 14.5.5.2 e nello standard C++ 11 nella sezione 14.5.6.2.

Ecco l'idea: per vedere se una funzione è più specializzata di un'altra, si inventa valori per ciascun parametro di modello per la prima funzione, quindi si vede se la seconda funzione potrebbe corrispondere alla firma risultante. Inoltre, inventi i valori per i parametri del modello della seconda funzione e verifica se la prima funzione corrisponde alla firma risultante. Se la seconda funzione può corrispondere alla prima, la seconda funzione non può essere più specializzata della prima. Se, oltre a ciò, la prima funzione non può corrispondere alla seconda, allora la prima deve essere più specializzata della seconda. Questo è il tuo caso.

2

Ecco un ancora ulteriore semplificazione del fenomeno:

#include <stdio.h> 

template<typename T> 
void foo(int arg) { 
    printf("a\n"); 
} 

template<typename T> 
void foo(T arg) { 
    printf("b\n"); 
} 

int main(int argc, char* argv[]) { 
    foo<int>(3); // prints "a" 
    foo(3);  // prints "b" 

    return 0; 
} 

parametri dei modelli possono sia essere passati esplicitamente tramite parentesi acute o possono essere risolti attraverso la deduzione. Se un parametro non è specificato esplicitamente, deve essere deducibile utilizzando gli argomenti della funzione.

Quindi, nel caso di foo(3), il modello 'a' non funzionerà, perché il parametro T non è esplicitamente specificato e non può essere dedotto. Nel caso di foo<int>(3), entrambi i modelli potrebbero funzionare. Infatti, se si commenta il modello 'a', una chiamata a foo<int>(3) stamperà "b". Quindi la domanda è: perché è preferibile il modello 'a'? La chiave qui è "ordinamento parziale":

http://publib.boulder.ibm.com/infocenter/lnxpcomp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8l.doc%2Flanguage%2Fref%2Fpartial_ordering_funct_templ.htm

Vedo ora che qualcun altro ha già risposto (sto male a rispondere alle domande in modo rapido), quindi ho intenzione di avvolgere proprio questa ora e dite che il modello 'a' è più specializzato come ha detto Vaughn.