2009-08-08 6 views
8

Wie einige von meinem Code implizite Konvertierung zwischen Matrizen verschiedenen Typen erforderlich (zB Matrix<int> zu Matrix<double>), definierte ich einen Templat-Copykonstruktor Matrix<T>::Matrix(Matrix<U> const&) statt den Standard Matrix<T>::Matrix(Matrix<T> const&):Templated Kopie-Konstruktor nicht mit spezifischem Templat-Typ

template <typename T> class Matrix { 
public: 
    // ... 
    template <typename U> Matrix(Matrix<U> const&); 
    // ... 
private 
    unsigned int m_rows, m_cols; 
    T *m_data; 
    // ... 
}; 

Mit einer geeigneten Typumwandlung, die dem Kopierkonstruktor hinzugefügt wurde, wurde diese Methode fehlerfrei zwischen Matrizen unterschiedlicher Typen konvertiert. Überraschenderweise schlägt es mit einem malloc-Fehler in der Situation fehl, in der ein einfacher Kopierkonstruktor funktionieren würde: where U == T. Sicher, das Überladen des Kopierkonstruktors mit der Standard-Signatur Matrix<T>::Matrix(Matrix<T> const&) löst das Problem.

Dies ist eine schlechte Lösung, da es zu einer doppelten Kopie des Kopierkonstruktorcodes kommt (wörtlich ein unverändertes Kopieren und Einfügen). Noch wichtiger, ich verstehe nicht, warum es einen doppelten malloc Fehler ohne den doppelten Code gibt. Außerdem, warum ist die extrem ausführliche template <typename T> template <typename U> Syntax erforderlich hier im Gegensatz zum Standard, und viel prägnanter, template <typename T, typename U>?

Vollständige Quelle der Vorlagenmethode, kompiliert mit G ++ v4.0.1 unter Mac OS 10.5.

template <typename T> template <typename U> Matrix<T>::Matrix(Matrix<U> const& obj) { 
    m_rows = obj.GetNumRows(); 
    m_cols = obj.GetNumCols(); 
    m_data = new T[m_rows * m_cols]; 

    for (unsigned int r = 0; r < m_rows; ++r) { 
     for (unsigned int c = 0; c < m_cols; ++c) { 
      m_data[m_rows * r + c] = static_cast<T>(obj(r, c)); 
     } 
    } 
} 

Antwort

13

Es schlägt fehl, weil eine Vorlage die implizite Deklaration eines Kopierkonstruktors nicht unterdrückt. Es dient als einfacher konvertierender Konstruktor, mit dem ein Objekt kopiert werden kann, wenn die Überladungsauflösung es auswählt.

Jetzt haben Sie wahrscheinlich Ihre Matrix irgendwo kopiert, die den implizit definierten Kopierkonstruktor verwenden würde, der eine flache Kopie erstellt. Dann würden die kopierte Matrix und die Kopie in ihrem Destruktor den gleichen Zeiger löschen.

Außerdem, warum ist die extrem ausführliche template <typename T> template <typename U> Syntax

erforderlich

Da es zwei Vorlagen beteiligt: ​​Die Matrix, die eine Klassenvorlage ist, und die Umwandlung Konstruktor Vorlage. Jede Vorlage verdient eine eigene Template-Klausel mit eigenen Parametern.

Sie sollten übrigens die <T> in Ihrer ersten Zeile loswerden. So etwas erscheint beim Definieren einer Vorlage nicht.

Dies ist eine schlechte Lösung, da es in der Groß-Duplizierung des Copy-Konstruktor Code führt

Sie eine Memberfunktion Vorlage definieren können, die sowohl von der Arbeit, und delegiert tun Konvertierungskonstruktor und der Kopierkonstruktor. Auf diese Weise wird der Code nicht dupliziert.


Richard einen guten Punkt in den Kommentaren, die mir meine Antwort ändern gemacht. Wenn die von der Vorlage generierte Kandidatenfunktion besser übereinstimmt als der implizit deklarierte Kopierkonstruktor, dann gewinnt die Vorlage und wird aufgerufen.Hier sind zwei typische Beispiele:

struct A { 
    template<typename T> 
    A(T&) { std::cout << "A(T&)"; } 
    A() { } 
}; 

int main() { 
    A a; 
    A b(a); // template wins: 
      // A<A>(A&) -- specialization 
      // A(A const&); -- implicit copy constructor 
      // (prefer less qualification) 

    A const a1; 
    A b1(a1); // implicit copy constructor wins: 
      // A(A const&) -- specialization 
      // A(A const&) -- implicit copy constructor 
      // (prefer non-template) 
} 

Eine Kopie Konstruktor einen nicht konstanten Referenz-Parameter haben, wenn eines ihrer Mitglieder hat

struct B { B(B&) { } B() { } }; 
struct A { 
    template<typename T> 
    A(T&) { std::cout << "A(T&)"; } 
    A() { } 
    B b; 
}; 

int main() { 
    A a; 
    A b(a); // implicit copy constructor wins: 
      // A<A>(A&) -- specialization 
      // A(A&); -- implicit copy constructor 
      // (prefer non-template) 

    A const a1; 
    A b1(a1); // template wins: 
      // A(A const&) -- specialization 
      // (implicit copy constructor not viable) 
} 
+0

Ihre Erklärung für den Malloc-Fehler klingt vor Ort. Gibt es einen bestimmten Grund dafür, warum Compiler keine Vorlagenelementfunktion als Kopierkonstruktor verwenden können? Danke für die Information und für den Hinweis zur Vermeidung von Code-Duplikation. –

+0

Wenn Sie diese Vorlage haben, ist dies noch keine Funktion. Nur die Verwendung mit einem Template-Argument erzeugt eine (member-) Funktion daraus (Spezialisierung genannt). Dies ist auch der Grund, warum Mitgliedervorlagen nicht virtuell sein können: Sie wissen nicht im Voraus, welche Funktionen daraus generiert werden. –

+0

Das macht sehr viel Sinn - das funktioniert nicht gerade deshalb, weil man Vorlagenparameter deklarieren oder die vollständige Quelle der Implementierung einbeziehen muss. Danke noch einmal. –

1

Ich bin nicht ganz klar, aus Ihrer Frage, aber Ich vermute, was passiert ist, dass der Standard-Kopierkonstruktor (der nur eine Memberly-Kopie verwendet) an einigen Stellen in Ihrem Code verwendet wird. Denken Sie daran, dass nicht nur der Code, den Sie tatsächlich schreiben, den Kopierkonstruktor verwendet, sondern auch der Compiler.

Verwandte Themen