2009-03-18 7 views
1

Je templated essaie de comprendre une syntaxe C++:question mineure concernant les fonctions templeted en classe

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*/ } 

Alors, je l'ai écrit un code comme celui-ci, et il arrive à compiler correctement sous Windows et Linux. Ce que je ne comprends pas, c'est pourquoi le constructeur de copie a deux modèles définis comme tels. Au fond, je devais expirment un peu avant de trouver la syntaxe correcte et je voudrais savoir pourquoi cette syntaxe particulière est correcte, et non quelque chose comme template<class T, class U>.

+0

Ceci n'est pas un constructeur de copie. Le compilateur se plaindra si vous essayez de transmettre la classe 'Foo ' par valeur. –

Répondre

4

Il doit avoir des clauses template distinctes pour chaque modèle concerné. Ici, deux modèles sont impliqués, que tous méritent leur (non vide) clauses de modèle:

  • Le modèle de classe Foo
  • Le modèle constructeur

Considérons ce cas qui échoue à cause de l'ambiguïté comme à l'endroit où le paramètre U appartient à

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

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

maintenant, ce qui est avec le paramètre U? Bien sûr, le compilateur pourrait deviner qu'il pourrait appartenir à f, mais deviner n'est pas ce que le compilateur aime :) La règle existante dit que selon l'imbrication des modèles, les clauses de modèles apparaissent dans le bon ordre. Tout est clair alors.

Même si l'on trouve une règle pour faire correspondre les paramètres aux arguments des modèles impliqués (pour l'instant je ne vois pas de difficulté réelle à le faire), cela serait incohérent. Parce qu'à partir de maintenant, une clause de modèle répertorie tous les paramètres que le modèle correspondant accepte. Tout comme une liste de paramètres de fonction. Si nous mettrions tout en une seule clause, que sémantique clair pourrait être cassé - pour ne pas mentionner que lorsque nous mettons la définition dans la classe à nouveau, tout d'un coup le modèle obtiendrait sa propre clause:

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

Il est beaucoup plus naturel quand nous avons des clauses de template séparées qui attrapent chacune leurs propres arguments.Ainsi, la syntaxe pour la définition erronée ci-dessus est

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

Maintenant, aussi le lecteur du code voit immédiatement que c'est une définition d'un modèle de membre, et non un second (potentiel accidentellement déclaré mais non utilisé) paramètre pour A.

7

Le premier modèle (avec le paramètre T) dit que la classe est structuré avec un paramètre T.

Le deuxième modèle (avec le paramètre U) indique que la fonction membre de la classe modélisée (avec le paramètre T) est modélisée avec le paramètre U - qui est le constructeur.

En fait, vous avez ici une classe template qui va générer autant de constructeurs de copies que de types utilisés comme paramètres du constructeur.

Dans le cas spécifique du constructeur de copie, vous ne devriez pas faire cela, mais à la place:

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*/ } 

Parce que vous exemple, il est copie pas constructeur, mais constructeur qui prend comme paramètre de type U: un constructeur de convertion .. C'est difficile à prévoir.

+0

Il peut y avoir des raisons légitimes de vouloir avoir une construction implicite d'un Foo à partir d'un Foo , mais vous avez raison de dire que ce n'est pas vraiment un constructeur de copie. –

+0

En fait, dans mon code actuel, j'ai les constructeurs de type Foo et Foo , mais le point est bien pris. Je suis content que le compilateur puisse travailler sur ce truc .. – Voltaire

+0

Oui c'est nécessaire parce que comme l'a dit T. McHenry, dans certains cas vous voulez ce comportement. Lorsque vous souhaitez gérer plusieurs types de types de nombres, par exemple, il peut être judicieux de faire en sorte que votre fonction de membre ait une seule définition pour tous les types, en ignorant s'il s'agit d'une classe ou d'un type de base. – Klaim

Questions connexes