2016-05-22 16 views
23

Ich habe die folgende Definition.Template Funktion Überladung 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; 
} 

Dann gibt es

1 

Von meinem Verständnis haben beide Versionen durch eine konstante Umwandlung zu gehen. Dann ist void foo(const T *s) spezialisierter und sollte aufgerufen werden. Der Compiler hat jedoch void foo(const T& s) gewählt. Was ist die Erklärung?

+2

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

+1

ich es kompilieren und kann immer noch nicht glauben, dass es wählt 1 – bolov

Antwort

15

Einige Leute haben darauf hingewiesen, dass die Parameter der Vorlagen, wie durch den Compiler gewählt

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

Hier fällt auf, dass der Compiler einen Zeiger eine konstante Referenz auf char, char*, für die erste Funktion und es ist erwartet . Es kann als eine Überraschung kommen, wenn Sie die für Ihren Fall fand es die erste Vorlage vorzieht, aber für die folgenden zwei, es wird die zweite

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

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

Also, natürlich bevorzugen, es wird Blick bei der Const manchmal. Warum war es in deinem Fall nicht? Weil es nur die const für eine Referenz betrachten wird, wenn die andere Funktion auch eine Referenz hat.

In Ihrem Fall char*-const char* ist es eine Zeiger-Konvertierung, aber lvalue-const lvalue, dann ist es nicht wirklich eine Umwandlung. Das Hinzufügen einer Konst mit einer Konst-Referenz wird von der Überladungsauflösung ignoriert, außer wenn beide Funktionen wie im obigen Fall über Referenzparameter verfügen.

+1

Was ich zuvor in der Antwort über "Lvalue Transformation" geschrieben habe, ist einfach Unsinn .. Ich war halb im Schlaf, als ich diese Antwort schrieb. –

+0

BTW vor http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1401 gab es eine nützliche Anmerkung, die "Die Bindung eines Verweises auf einen Ausdruck, der Bezug-kompatibel ist mit zusätzlicher Qualifikation beeinflusst den Rang einer Standardkonversion, siehe 13.3.3.2 [over.ics.rank] und 8.5.3 [dcl.init.ref]. ". Ich hätte es vorgezogen, wenn sie diesen Satz beibehalten und neu formuliert hätten. Beachten Sie die Formulierung "beeinflusst den Rang einer Standardkonvertierung". Das ist nicht zufällig, sondern betont, dass es sich nur auf das Ranking auswirkt und keine Conversion darstellt. –

5

Der Grund ist, weil s ein nicht-konstanter Zeiger ist, also int * const & ist eigentlich eine bessere Übereinstimmung als int const *, weil es const nicht zum Zeigertyp hinzufügen muss.

Wenn s const qualifiziert wäre, dann wäre es eine exakte Übereinstimmung für die T const * Version.

2

beide Versionen müssen eine const-Konvertierung durchlaufen.

Die erste muss nicht konvertiert werden. Für Vorlage Parameter const T& mit Vorlage Argument char *, T wird als char* abgeleitet und dann die Art der Funktion Parameter wird char* const &, so ist es perfekt. Das Funktionsargument wird gebunden, um auf const (T ->const T&, d. H. char* bis char* const&) Bezug zu nehmen, constqualified ist keine Konvertierung.

Für die zweite ein, die Template-Parameter const T* mit Template-Argument char * wird T als char abgeleitet werden und dann wird die Art der Funktionsparameter const char* sein und qualification conversion benötigt wird char*-const char* zu konvertieren.

Von Ihrem Kommentar

Beide Routinen hinzufügen Low-Level-const s.

Der erste fügt const zu s hinzu, aber der zweite ist nicht. Es fügt const hinzu, was s Punkte auf, nicht s selbst ist. Das ist der Unterschied.

+0

Er hätte erwarten können, dass 'char * const &' für ein argument 'char *' auch eine const-Konvertierung benötigt. Intuitiv ist das nicht allzu weit zu erwarten, IMO. –

+1

Warum wird 'const T & s' mit' T = char * 'zu' char * const & '? Ich meine, warum die Position von "const" sich ändert. –

+0

@ JohannesSchaub-litb, ja das ist eigentlich was ich erwartet habe. Beide Routinen fügen Low-Level const zu "s" hinzu. Aber warum ist diese Umwandlung hier nicht passiert? –

4

Ich habe die const ohne Änderung der Semantik des Codes bewegt, nur damit Sie nicht überrascht, wenn die Position des const „Änderungen“ später.

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

Überlastungs 1 haben T herzuleiten char* so die Art der zu s wird char * const & (Verweis auf const Zeiger auf nicht-const char) sein. Eine solche Referenz kann ohne Konvertierung an den Argumenttyp (char *; Zeiger an nicht-const char) gebunden werden.

Überlastungs 2 wird T herzuleiten char so die Art der zu s wird char const * (Zeiger char const) sein. Dies führt zu einer Qualifizierungskonvertierung von dem Argumenttyp (char *; Zeiger zu nicht-konstant char) zu dem Parametertyp (char const *; Zeiger zu const char).

Ein illustratives Beispiel des Prinzips in Überlast 1 ist folgende:

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

Binden eines const Verweis auf eine nicht-const Entität keine Umwandlung beteiligt, weil es die Bezugnahme der, dass die Qualifikation fügt Es ist nicht die Qualifikation, die der Entität hinzugefügt wird, um sie dem Referenztyp zuzuordnen.

1

Der Grund ist, weil, wenn das Argument ein Zeiger ist, müssen Sie .. in einem Zeiger übergeben So:

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

Aber wenn Ihr Argument ein refrence ist, können Sie einfach den Parameter übergeben, wie es ist dies wie:

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