2016-05-06 7 views
7

Ich habe eine Template-Funktion, func:Wie mache ich die Vorlagentyp-Abzug arbeiten mit Referenzen?

template<typename T> 
void func(T p) { f(p); } 

Und eine Reihe von Funktionen f:

f(SomeType&); 
f(int); 
... 

Wenn ich instanziiert die Template-Funktion, func, eine Referenz als Funktion Argument p, ohne explizit Wenn der Vorlagenparameter T angegeben wird, ist der abgeleitete Typ nicht der Referenztyp von p, sondern der Typ ist ein Verweis auf zum Beispiel:

SomeType s; 
SomeType& ref_to_s = s; 
func(ref_to_s);    // Type deduction results in: func<SomeType>(ref_to_s) 
func<SomeType&>(ref_to_s); // Need to explicitly specify type to work with reference 

Also, meine Fragen sind:

  • Warum der Compiler nicht den Referenztyp SomeType& in der obigen Situation abzuleiten?
  • Gibt es eine Möglichkeit, Vorlagenfunktion func zu definieren, so dass Typableitung mit einem Referenztyp ohne explizite Angabe des Vorlagenparameters T funktioniert?

Um klar zu sein, würde Ich mag etwas mit den beiden Arbeits (Funktionen f siehe oben):

func(ref_to_s); // Calls func<SomeType&>(ref_to_s) 
func(1);   // Calls func<int>(1) 
+4

Offensichtlich funktioniert das Template Argument Abzug. – 101010

Antwort

9

Die Antwort auf den ersten Teil besteht darin, dass das Initialisieren eines Objekts aus einem Wert und das Binden eines Verweises auf einen Wert als gleich gut betrachtet wird (beide sind "exakte Übereinstimmungen" in Überladungsauflösung) und daher nicht die Bedeutung eines Parameters abgeleitet. Sie müssen es vielmehr genau angeben. Für ein ähnliches Beispiel eine hypothetische Konstruktion

T x = f(); 

wo T ist (für die Dauer dieses Gedankenexperiment) etwas betrachten Sie angeblich sind abzuleiten. Was sollte x sein - ein Objekt oder eine Referenz? Wovon würde es abhängen? Der Ausdruck auf der rechten Seite hat natürlich einen Wert und der Typ dieses Wertes ist immer ein Objekttyp. Wie sollten Sie sich entscheiden? Sie könnten die Wertkategorie des Ausdrucks abfragen, aber das ist zu subtil. Also statt, Sie muss sagen, was Sie wollen:

T x = f(); // x is an object 
T & r = f(); // r is a reference 

In C++, dieser Mechanismus tatsächlich verfügbar ist, wenn Sie T mit auto ersetzen. Die gleichen Regeln gelten für die Ableitung von Vorlagenargumenten für Funktionsvorlagen.

Jetzt auf, wie Sie Ihr Problem lösen.Das übliche Idiom ist die Funktion Vorlage immer nimmt einen Verweis zu machen, und dann weiterleiten, das Argument zu dem inneren Ruf:

template <typename T> 
void func(T && t) 
{ 
    f(std::forward<T>(t)); 
}; 

Jetzt func ‚s-Parameter ist immer eine Referenz in jeder Spezialisierung (aber dank einem seltsame Regel namens "Reference Collapsing" kann es entweder ein Lvalue oder eine Rvalue-Referenz sein, und dieser Wert wird mit der gleichen Wert Kategorie Gruppe zu f weitergeleitet, und diese Überladung Auflösung auf f ermöglicht Lvalue-Referenz Überladungen wann ausgewählt werden Das ursprüngliche Argument für func war ein Lvalue.

+1

Universalreferenz und Referenzzusammenbruch sind mir jetzt klarer; Vielen Dank für Ihre ausführliche Antwort. – shrike

+0

"Der Ausdruck auf der rechten Seite hat natürlich einen Wert und der Typ dieses Wertes ist immer ein Objekttyp." Und was, wenn f() eine Referenz zurückgibt? – bipll

+6

@bipll: So etwas gibt es nicht. Ein Funktionsaufruf-Ausdruck * ist ein Ausdruck * und Ausdrücke haben immer Objekttypen. Die Referenzqualifikation für den Rückgabewert der Funktion bestimmt den Werttyp des Ausdrucks. Eine Funktion kann nicht "eine Referenz zurückgeben", das ist nur ein schlampiger Umgangssprache. –

7

Verwendung Spedition Verweis:

template<typename T> 
void func(T && p) //notice && 
{ 
    f(std::forward<T>(p)); //notice std::forward 
} 

bitte Forwarding-Referenz Jetzt suchen (oder Universal-Referenz, wie es zuvor genannt) sowie std::forward auf dieser Website, um mehr über sie zu wissen.

0

Betrachtet man das folgende Beispiel:

template<typename T> 
void f(T value, T& ref) {// fake swapping 
T temp = ref; 
ref = value; 
value = temp; 
} 

Wenn Compiler den Typ der T abzuleiten versucht, die vernünftige Sache zunächst zu tun ist möglich, Referenzen von „Wert“ und „ref“ entfernen, so dass

int i = 1; 
int j = 2; 
f(i, j); //value of j becomes 1. 
//up to here, value of i is still 1 

Sonst wird es ziemlich bizarr. Wenn T ansonsten zu T & abgeleitet wird, ist der Typ von f (i, j) dann äquivalent zu f (int &, int & &). Angenommen, T & & kollabiert zu T &, dann wird es f (int &, int &). Das widerspricht der Absicht des Programmierers.

Verwandte Themen