2009-03-18 7 views
1

ich einig C++ Syntax zu verstehen versuchen:Minor Frage templeted Funktionen in Templat-Klasse in Bezug auf

template<class T> 
class Foo 
{ 
    Foo(); 

    template<class U> 
    Foo(const Foo<U>& other); 
}; 

template<class T> 
Foo<T>::Foo() { /*normal init*/ } 

template<class T> 
template<class U> 
Foo<T>::Foo(const Foo<U>& other) { /*odd copy constructed Foo*/ } 

So schrieb ich Code wie dies, und es geschieht in Windows und Linux in Ordnung zu kompilieren. Was ich nicht verstehe ist, warum der Kopierkonstruktor zwei Vorlagen hat, die so definiert sind. Im Grunde hatte ich ein bisschen expirment, bevor ich die richtige Syntax gefunden und ich würde gerne wissen, warum diese spezielle Syntax korrekt ist, und nicht so etwas wie template<class T, class U>.

+0

Dies ist * nicht * ein Kopierkonstruktor. Der Compiler wird sich beschweren, wenn Sie versuchen, die Klasse 'Foo ' nach Wert zu übergeben. –

Antwort

4

Es muss separate template Klauseln für jede Vorlage, die beteiligt ist. Hier sind zwei Vorlagen beteiligt sind, dass alle verdienen ihre (nicht leer) Template-Klauseln:

  • Die Klassenvorlage Foo
  • Der Konstruktor Vorlage

diesen Fall betrachtet, die wegen der Mehrdeutigkeit versagt , wo der Parameter U zu

gehört jetzt
template<typename T> 
struct A { 
    template<typename U> void f(); 
}; 

template<typename T, typename U> 
void A<T>::f() { } 

, was ist mit dem Parameter U up? Sicher, der Compiler könnte erraten, dass es zu f gehören könnte, aber Ratespiel ist nicht das, was der Compiler gefällt :) Die vorhandene Regel besagt, dass Template-Klauseln in der richtigen Reihenfolge angezeigt werden, abhängig von der Verschachtelung von Vorlagen. Alles ist dann klar.

Selbst wenn man mit einer Regel aufkommt, wie man die Parameter mit Argumenten der beteiligten Templates vergleicht (bisher sehe ich keine wirkliche Schwierigkeit darin), wäre das inkonsistent. Denn ab sofort listet eine Template-Klausel alle Parameter auf, die die entsprechende Vorlage akzeptiert. Ähnlich wie eine Funktionsparameterliste. Wenn wir alles in einer Klausel, dass klare semantische gebrochen werden konnte setzen würde - nicht zu erwähnen, dass, wenn wir wieder die Definition in die Klasse setzen, ganz plötzlich der Vorlage würde eine eigene Klausel erhalten:

// provides arguments for A's parameters, then for f ones 
// when it's called 
A<int> a; 
a.f<bool>(); 

Es ist viel natürlicher, wenn wir separate Template-Klauseln haben, die jeweils ihre eigenen Argumente erfassen.So ist die Syntax für die oben falsche Definition

template<typename T> 
template<typename U> 
void A<T>::f() { } 

nun auch die Leser des Codes sofort sieht, dass dies eine Definition eines Mitglieds Vorlage, und kein (Potential versehentlich deklariert, aber nicht verwendet) zweiter Parameter für A.

7

Die erste Vorlage (mit dem Parameter T) sage, dass die Klasse wird mit einem Parameter T vorgestempelt.

Die zweite Vorlage (mit Parameter U) besagt, dass die Elementfunktion der Vorlagenklasse (mit Parameter T) mit dem Parameter U - das ist der Konstruktor - templatiert wird.

Tatsächlich haben Sie hier eine Vorlagenklasse, die so viele Kopierkonstruktoren erzeugt, wie Typen, die als Parameter des Konstruktors verwendet werden.

Im speziellen Fall von Copykonstruktor, Sie sollten dies nicht tun, sondern:

template<class T> 
class Foo 
{ 
    Foo(); 

    Foo(const Foo<T>& other); 
}; 

template<class T> 
Foo<T>::Foo() { /*normal init*/ } 

template<class T> 
Foo<T>::Foo(const Foo<T>& other) { /*odd copy constructed Foo*/ } 

Weil in ihrem Beispiel ist es nicht Konstruktor aber Konstruktor kopieren, Typen U als Parameter übernehmen: ein convertion Konstruktor .. Das ist schwer vorherzusagen.

+0

Es kann legitime Gründe geben, implizite Konstruktion eines Foo von einem Foo zu haben, aber Sie haben Recht, dass es dann nicht wirklich ein Kopierkonstruktor ist. –

+0

Eigentlich habe ich in meinem tatsächlichen Code beide Foo und Foo Typ Konstruktoren, aber der Punkt ist gut gemacht. Ich bin froh, dass der Compiler dieses Zeug ausarbeiten kann. – Voltaire

+0

Ja, es ist notwendig, weil wie T. McHenry sagte, in einigen Fällen wollen Sie dieses Verhalten. Wenn Sie beispielsweise mehrere Arten von Zahlentypen verwalten möchten, ist es möglicherweise eine gute Idee, dass Ihre Member-Funktion so konfiguriert wird, dass nur eine Definition für alle Typen vorhanden ist. Dabei wird ignoriert, ob es sich um eine Klasse oder einen Basistyp handelt. – Klaim

Verwandte Themen