2009-08-08 5 views
8

Comme certains de mon code nécessaire conversion implicite entre matrices de types différents (par exemple Matrix<int> à Matrix<double>), je défini un constructeur de copie basé sur un modèle Matrix<T>::Matrix(Matrix<U> const&) au lieu du Matrix<T>::Matrix(Matrix<T> const&) standard:copie constructeur Templated échoue avec le type basé sur un modèle spécifique

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

Avec un doublon approprié ajouté au constructeur de copie, cette méthode a parfaitement converti entre matrices de types différents. Étonnamment, il échoue avec une erreur malloc dans la situation même où un simple constructeur de copie fonctionnerait: où U == T. Effectivement, surcharger le constructeur de copie avec la signature par défaut Matrix<T>::Matrix(Matrix<T> const&) résout le problème.

Ceci est une mauvaise solution, car il en résulte la duplication en gros du code du constructeur de copie (littéralement un copier-coller inchangé). Plus important encore, je ne comprends pas pourquoi il y a une erreur sans double malloc sans le code en double. En outre, pourquoi la syntaxe template <typename T> template <typename U> extrêmement verbeuse est-elle requise ici plutôt que la norme, et beaucoup plus succincte, template <typename T, typename U>?

Source complète de la méthode basée sur un modèle, compilée à l'aide de G ++ v4.0.1 sur 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)); 
     } 
    } 
} 

Répondre

13

Échec car un modèle ne supprime pas la déclaration implicite d'un constructeur de copie. Il servira de simple constructeur de conversion, qui peut être utilisé pour copier un objet lorsque la résolution de surcharge le sélectionne.

Maintenant, vous avez probablement copié votre matrice quelque part, ce qui utiliserait le constructeur de copie implicitement défini qui effectue une copie à plat. Ensuite, la matrice copiée et la copie supprimeraient le même pointeur dans leur destructeur.

En outre, pourquoi la syntaxe est template <typename T> template <typename U> extrêmement bavard nécessaire

Parce qu'il ya deux modèles impliqués: La matrice, qui est un modèle de classe, et le modèle de constructeur de conversion. Chaque modèle mérite sa propre clause de modèle avec ses propres paramètres.

Vous devriez vous débarrasser du <T> dans votre première ligne, soit dit en passant. Une telle chose n'apparaît pas lors de la définition d'un modèle.

Ceci est une mauvaise solution, car elle entraîne la duplication de gros du code constructeur de copie

Vous pouvez définir un modèle de fonction membre, qui fera le travail, et délégué à la fois la constructeur de conversion et le constructeur de copie. De cette façon, le code n'est pas dupliqué. Richard a fait un bon point dans les commentaires qui m'ont fait amender ma réponse. Si la fonction candidate générée à partir du modèle correspond mieux que le constructeur de copie implicitement déclaré, le modèle "gagne" et il sera appelé.Voici deux exemples courants:

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

Un constructeur de copie peut avoir un paramètre de référence non-const aussi, si l'un de ses membres a

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

Votre explication de l'erreur malloc sonne sur place. Existe-t-il une raison spécifique pour laquelle les compilateurs ne peuvent pas utiliser une fonction de membre de modèle en tant que constructeur de copie? Merci pour les informations et pour les conseils pour éviter la duplication de code. –

+0

Si vous avez ce modèle, ce n'est pas encore une fonction. Seul son utilisation avec un argument de template génère une fonction (membre) hors de lui (appelée une spécialisation). C'est également la raison pour laquelle les modèles de membres ne peuvent pas être virtuels: vous ne savez pas à l'avance quelles sont les fonctions qui en sont générées. –

+0

Cela a beaucoup de sens - cela ne fonctionne pas pour la raison même que l'on doit soit renvoyer les paramètres du modèle de déclaration, soit inclure la source complète de l'implémentation. Merci encore. –

1

Je ne suis pas tout à fait claire de votre question, mais Je suspecte que ce qui se passe est que le constructeur de copie par défaut (qui fait une copie de memberwise seulement) est utilisé dans quelques endroits dans votre code. Souvenez-vous que non seulement le code que vous écrivez utilise le constructeur de la copie, mais le compilateur l'utilise également.

Questions connexes