2016-05-22 21 views
23

Ho la seguente definizione.Sovraccarico funzione template in C++

using namespace std; 

template <typename T> 
void foo(const T &s) { 
    cout << 1; 
} 

template <typename T> 
void foo(const T *s) { 
    cout << 2; 
} 

int main(int argc, const char * argv[]) { 
    char str[] = "ss"; 
    char *s = str; 
    foo(s); 

    return 0; 
} 

Poi emette

1 

Dalla mia comprensione, entrambe le versioni devono passare attraverso una conversione const. Quindi void foo(const T *s) è più specializzato e dovrebbe essere invocato. Tuttavia il compilatore ha scelto void foo(const T& s). Qual è la spiegazione?

+2

'const T & s' ->' T const e s' -> 'T = char *' -> 'char * const &' –

+1

sto compilando e ancora non riesco a credere che sceglie 1 – bolov

risposta

15

Alcune persone hanno fatto notare che i parametri dei modelli a scelta dal compilatore

void f(char * const&) 
void f(const char *); 

Qui, si noti che il compilatore si aspetta un puntatore a char, char*, per la prima funzione ed è un riferimento const . Essa può venire come una sorpresa, se avete trovato che per il vostro caso si preferisce il primo modello, ma per i successivi due, si preferirà la seconda

template <typename T> 
void foo(const T& s) { 
    cout << 1; 
} 

template <typename T> 
void foo(T &s) { 
    cout << 2; 
} 

Così, naturalmente, è sarà sguardo a volte const. Perché non nel tuo caso? Perché vedrà solo il riferimento a const se l'altra funzione ha anche un riferimento.

Nel tuo caso, char*-const char* Si tratta di una conversione puntatore, ma lvalue-const lvalue, in realtà non è una conversione. L'aggiunta di una const tramite un riferimento const viene ignorata dalla risoluzione di sovraccarico, tranne quando entrambe le funzioni hanno parametri di riferimento come nel caso precedente.

+1

quello che ho scritto in precedenza nella risposta a proposito di "trasformazione lvalue" è un assurdità. Ero addormentato quando ho scritto questa risposta. –

+0

BTW prima di http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1401 c'era una nota utile che diceva "L'associazione di un riferimento a un'espressione che è compatibile con riferimento con una qualifica aggiunta influenza il rango di una conversione standard, vedi 13.3.3.2 [over.ics.rank] e 8.5.3 [dcl.init.ref]. ". Avrei preferito se avessero tenuto questa frase e riformulata. Notare che la dicitura "influenza il rango di una conversione standard" .. non è casuale ma sottolinea che influisce solo sulla classifica e non costituisce una conversione autonoma. –

5

Il motivo è perché s è un puntatore non const, quindi int * const & è in realtà una corrispondenza migliore di int const * perché non deve aggiungere const al tipo di puntatore.

Se s erano const qualificati, sarebbe una corrispondenza esatta per la versione T const *.

2

entrambe le versioni devono passare attraverso una conversione const.

Il primo non ha bisogno di conversione. Per il parametro di modello con argomento modello char *, verrà dedotto come char* e il tipo di parametro di funzione sarà char* const &, quindi è perfetto. L'argomento della funzione sarà associato al riferimento a const (T ->const T&, ad esempio char* a char* const&), const qualificato non è la conversione.

Per il 2 °, il parametro di template const T* con template argomento char *, T saranno dedurre come char e quindi il tipo di parametro di funzione sarà const char*, e qualification conversion è necessario per convertire char*-const char*.

Dal tuo commento

Entrambe le routine di aggiungere a basso livello const a s.

La prima è l'aggiunta di una const-s, ma il secondo non lo è. Aggiunge const a quello s punti, non s stesso. Questa è la differenza.

+0

Avrebbe potuto aspettarsi che 'char * const &' per un argomento' char * 'avrebbe avuto bisogno anche di una conversione costante. Intuitivamente, non è troppo lontano per aspettarsi, IMO. –

+1

Perché 'const T & s' con' T = char * 'andrà a' char * const & '? Intendo perché la posizione di "const" cambia. –

+0

@ JohannesSchaub-litb, sì, questo è quello che mi aspettavo. Entrambe le routine aggiungono const di basso livello a 's'. Ma perché questa conversione non è avvenuta qui? –

4

Ho spostato il const senza modificare la semantica del codice, solo così non sei sorpreso quando la posizione di const "cambia" in seguito.

template <typename T> 
void foo(T const &s) { 
    cout << 1; 
} 

template <typename T> 
void foo(T const *s) { 
    cout << 2; 
} 

char *x = "x"; 
foo(x); 

Overload 1 dovrà dedurre T essere char* così il tipo di s sarà char * const & (riferimento al puntatore const a non-const char). Tale riferimento può associare al tipo di argomento (char *; puntatore a non-const char) senza alcuna conversione.

Overload 2 dovrà dedurre T essere char così il tipo di s sarà char const * (puntatore a const char). Ciò comporta una conversione di qualifica dal tipo di argomento (char *; puntatore a non-const char) al tipo di parametro (char const *; puntatore a const char).

Un esempio più illustrativo del principio sovraccarico 1 è il seguente:

int n = 42; 
int const &i = n; 

Binding un riferimento const ad un'entità non-const non comporta una conversione, perché è il riferimento che aggiunge la qualifica , non è la qualifica che viene aggiunta all'entità per abbinarla al tipo di riferimento.

1

Il motivo è perché quando l'argomento è un puntatore, si deve passare in un puntatore .. Ti piace questa:

// Example program 
#include <iostream> 
#include <string> 

using std::cout; 
using std::endl; 

void foo(int *a) 
{ 
    cout << *a << endl; 
} 

int main() 
{ 
    int a = 5; 
    foo(&a); 
} 

Ma se il tuo argomento è un refrence si può semplicemente passare il parametro come è in questo modo:

// Example program 
#include <iostream> 
#include <string> 

using std::cout; 
using std::endl; 

void foo(int &a) 
{ 
    cout << a << endl; 
} 

int main() 
{ 
    int a = 5; 
    foo(a); 
} 
Problemi correlati