2016-05-22 17 views
23

Mam następującą definicję.Przeciążanie funkcji szablonu w 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; 
} 

następnie przesyła je

1 

Z mojego zrozumienia, obie wersje mają przejść przez const nawrócenia. Następnie void foo(const T *s) jest bardziej wyspecjalizowany i powinien zostać wywołany. Jednak kompilator wybrał void foo(const T& s). Jakie jest wytłumaczenie?

+2

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

+1

jestem kompilowanie go i nadal nie mogę uwierzyć, że wybiera 1 – bolov

Odpowiedz

15

Niektórzy ludzie zwracają uwagę, że parametry szablonów, jak wybrane przez kompilator

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

tu zauważyć, że kompilator spodziewa się wskaźnik do char, char*, dla pierwszej funkcji i jest to const odniesienia . Może to dziwić, jeśli okazało się, że w Twoim przypadku preferuje pierwszy szablon, ale w kolejnych dwóch, będzie to wolą drugi

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

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

Tak, oczywiście, to będzie wygląd czasami w const. Dlaczego nie w twoim przypadku? Ponieważ będzie wyglądać tylko na odniesienie const, jeśli druga funkcja ma również odniesienie.

W twoim przypadku, od char* do const char* jest to wskaźnik konwersji, ale z lvalue do const lvalue, to nie jest faktycznie konwersji. Dodawanie stałej przez odniesienie do stałej jest ignorowane przez rozdzielczość przeciążania, z wyjątkiem sytuacji, gdy obie funkcje mają parametry referencyjne, jak w powyższym przypadku.

+1

to, co wcześniej napisałem w odpowiedzi na temat "transformacji lwartości", jest zwykłym nonsensem. Zasnąłem, gdy pisałem tę odpowiedź. –

+0

BTW przed http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1401 pojawiła się użyteczna notatka, w której napisano: "Powiązanie odwołania z wyrażeniem zgodnym z odniesieniami z dodaną kwalifikacją wpływa na rangę standardowej konwersji, zobacz 13.3.3.2 [over.ics.rank] i 8.5.3 [dcl.init.ref]. ". Wolałbym, aby zachowali to zdanie i przeredagowali. Zwróć uwagę, że sformułowanie "wpływa na stopień standardowej konwersji". Nie jest przypadkowe, ale podkreśla, że ​​ma ono wpływ tylko na ranking i samo w sobie nie stanowi konwersji. –

5

Powód jest taki, że s jest wskaźnikiem o stałej długości, więc int * const & jest w rzeczywistości lepszym dopasowaniem niż int const *, ponieważ nie musi dodawać const do typu wskaźnika.

Jeśli s było const qualified, to byłby dokładnym dopasowaniem do wersji T const *.

2

obie wersje muszą przejść konwersję stałej.

Pierwsza nie wymaga konwersji. Dla parametru szablonu const T& z argumentem szablonu char *, T zostanie wyprowadzony jako char*, a następnie typ parametru funkcji będzie char* const &, więc idealnie pasuje. Argument funkcji będzie powiązany z const (T ->const T&, tj. char* na char* const&), const const nie jest konwersją.

Dla 2 jeden parametr szablonu const T* z szablonu argumentu char *, T będzie wyprowadzona jako char a następnie rodzaj parametru funkcji będzie const char* i qualification conversion jest potrzebny do konwersji char* do const char*.

Z Twojego komentarza

Obie procedury dodać niskopoziomowe const do s.

The 1st jeden jest dodanie const do s, ale 2 jeden nie jest. Dodaje się const do tego, co oznacza s, a nie sam s. To jest różnica.

+0

Mógłby się spodziewać, że 'char * const &' dla argmentu 'char *' będzie również wymagało konwersji stałej. Intuicyjnie, to nie jest zbyt daleko, aby się spodziewać, IMO. –

+1

Dlaczego 'const T & s' z' T = char * 'przejdzie do' char * const & '? Mam na myśli, dlaczego zmienia się pozycja "const". –

+0

@ JohannesSchaub-litb, tak, to jest to, czego się spodziewałem. Obie procedury dodają stałą niskopoziomową do 's'. Ale dlaczego ta konwersja się tutaj nie wydarzyła? –

4

Przesunąłem const bez zmiany semantyki kodu, po prostu nie zdziwisz się, gdy pozycja "const" zmieni się "później.

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); 

termicznego 1 muszą wywnioskować T być char* więc typ s będzie char * const & (odniesienie do const wskaźnik do const char). Takie odniesienie może być powiązane z typem argumentu (char *; wskaźnik na niestanowiący char) bez jakiejkolwiek konwersji.

przeciążeniem 2 będzie musiał wyprowadzić T być char więc rodzaj s będzie char const * (wskaźnik do const char). Powoduje to przekształcenie kwalifikacji z typu argumentu (char *; wskaźnik na niestanowiący char) na typ parametru (char const *; wskaźnik na const char).

bardziej obrazowy przykład zasady przeciążenie 1 jest następująca:

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

Oprawa const odwołanie do const podmiotu nie wymaga konwersji, ponieważ jest to odniesienie, które dodaje kwalifikacje , nie jest to kwalifikacja dodawana do encji, aby dopasować ją do typu referencyjnego.

1

Powodem jest fakt, gdy argument jest wskaźnikiem, trzeba zdać się wskaźnik .. Jak to:

// 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); 
} 

Ale jeśli argument jest Referencje można po prostu przekazać parametr jak to jest tak:

// 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); 
} 
Powiązane problemy