Déjà, ce n'est pas Code::Blocks qui t'envoie l'erreur (lui, il se contente d'afficher l'erreur envoyée par le compilateur), mais bon... Ce n'est qu'un détail
Quant à ton problème, il est simple : si tu veut fournir une spécialisation pour une fonction membre d'une classe template, tu dois explicitement indiquer qu'il s'agit d'une classe template, même si tous les paramètres template ont été fournis. Cela donnera donc quelque chose ressemblant à
error : specializing member 'point::affiche' requires 'template<>' syntax
se traduisant (littéralement) en
erreur: spécialiser le mobre 'point::affiche' requière la syntaxe 'template<>'
Quelques remarques au passage:
- PAS de using namespace std; dans un fichier d'en-tête (idéalement, il ne faudrait jamais l'utiliser)
- Si tu n'implémente pas une fonction de ta classe template directement dans la déclaration de ta classe, tu devrais la déclarer comme inline, parce qu'elle ne sera pas implicitement considérée comme telle et que, du coup, chaque unité de compilation l'utilisant en contiendra le code binaire exécutable, ce qui énervera l'éditeur de liens
- Une ligne, une instruction : T x; T y; devrait donc se faire sur deux lignes
- Eviter le transtypage "barbare à la C" et préférer les transtypage nommés comme static_cast (dans le cas présent
-Pourquoi utiliser une fonction très spécifique afficher au lieu d'utiliser l'opérateur << qui peut être adapté à n'importe quel flux de sortie ?
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Ensuite, c'est ridicule de définir un type pour des coordonnées numériques. Autant implémenter directement ta classe avec int ou double comme type des coordonnées
Puis, ceci est à bannir :
foo(T bar = value); // chez toi, à la ligne 9
Tu ne sais pas à l'avance quel type l'utilisateur pourrait envoyer à ce template, donc il ne faut pas l'initialiser avant, au risque de se retrouver avec quelque chose comme ça :
struct MyStrcut {
...
};
foo(MyStruct bar = 0);
Ce qui, bien sûr, ne fonctionne pas.
Après, ceci n'a pas de sens avec ce que tu souhaites faire :
template <class T> class point;
Ton compilateur s'attend à recevoir un objet en paramètre de template, mais là tu lui donnes le type char.
Pour préciser que l'on attend un type, et pas un objet, on utilise typename :
template <typename T> class point;
Et enfin, tu as fais une erreur de frappe :
point (T abs = 0; T ord = 0);
Remplace par :
point (T abs = 0, T ord = 0);
Cela devrait fonctionner à présent.
PS: grillé
- Edité par vanaur 25 août 2018 à 16:18:10
Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...
Au fait: donner une valeur par défaut pour les deux paramètres dans le constructeur n'est pas logique, car cela permet d'utiliser la syntaxe
Point{} (utilise les deux valeurs par défaut)
Point{x} (utilise la valeur de x pour abs, la valeur par défaut pour ord)
Point{x, y} (utilise la valeur de x pour abs et la valeur de y pour ord)
Or, sur les trois possibilités, seules la première et la dernière ont réellement du sens et seront effectivement utilisées: soit on crée notre point avec les valeurs par défaut (abs = 0 et ord = 0), soit crée un point en fournissant son abscisse ET son ordonnée.
Et, il se fait que l'on va tout faire pour respecter la règle qui consiste à
rendre l'interface de vos classes faciles à utiliser correctement et difficiles à utiliser de manière incorrecte
Malheureusement pour nous, permettre à l'utilisateur de créer un point en ne donnant que la valeur de son abscisse représente bel et bien une possibilité d'utiliser notre classe de manière incorrecte.
Du coup, bien que cela soit plus embêtant, il faut veiller à ne fournir que les deux constructeurs qui nous intéressent réellement sous une forme qui serait du coup proche de
template <typename T>
class Point{
public:
Point(): Point{0,0}{} // (*)
Point(T abs, T ord):abs{abs},ord{ord}{ // (**)
}
inline void affiche() const;
private: // (***)
T abs;
T ord;
};
(*) 1- Depuis 2011, il est possible de déléguer l'appel d'un constructeur à un autre
2- Depuis 2011 toujours, il est possible d'utiliser une syntaxe "normalisée" utilisant les accolade lorsque l'on veut définir la valeur d'une variable lors de sa création
(**) reprend tout ce qui est dit en (*) et utilise la liste d'initialisation, qui devrait être préférée (depuis toujours) pour le constructeur (le corps du constructeur ne contenant que ce qui ne peut pas être fait dans la liste d'initialisation).
Car la liste d'initialisation fonctionnera toujours alors que l'affectation dans le corps du constructeur peut -- dans certaines circonstances -- être purement et simplment impossible
(***) Par habitude, on préférera exposer les parties publiques en premier, suivies par les parties protégées et, enfin, par les parties privées.
Cela permet à l'utilisateur d'avoir les informations qui l'intéresse (les parties publique) en premier, et donc d'avoir "plus d'informations utiles" en haut du fichier d'en-tête, sans devoir aller chercher "dans les profondeurs" du fichiers.
Bien sur, il y aura toujours "quelques cas" dans lesquels nous ne pourrons pas nous y tenir, mais c'est malgré tout une habitude "relativement saine" à prendre
- Edité par koala01 25 août 2018 à 16:44:56
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Que signifie "foo" ? C'est un terme usuel pour désigner un constructeur ?
"foo" est simplement un mot du jargon informatique qui désigne tout et rien, on l'utilise pour faire une généralité afin de ne pas être embrouillé avec d'autres choses ;
T foo;
if (foo == bar);
T foo();
#include <foo>
...
Tout comme "T", qui désigne souvent un type.
Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...
-Qu'entends tu exactement par déléguer l'appel d'un constructeur à un autre ? Serait-ce similaire à la sur-définition de fonction ?
C'est la fameuse ligne
Point(): Point{0,0}{} // (*)
Point() déclare (et implémente) un constructeur de la classe Point qui ne nécessite aucun paramètre. Les deux points : introduisent la liste d'initialisation et Point{0,0} fait appel (délèguent la création du point) au constructeur de la classe qui nécessite deux paramètres pour fonctionner.
Enfin la paire d'accolades qui suit (celle qui est vide qui est vide) indique le "corps de la fonction", qui n'a ... plus rien à faire, vu que tout a été "délégué" au constructeur qui a besoin de deux paramètres
Cette manière de faire n'est apparue qu'en C++11, parce que, avant cela, nous ne pouvions faire appel qu'aux constructeurs des données membres (ou au constructeur de la classe de base, dans le cadre d'un héritage) de la classe dans la liste d'initialisation.
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...
Le meilleur moyen de prédire l'avenir, c'est de l'inventer | N'oubliez pas [résolu] et +1 | Excusez mon ôrtograffe, j'essaie de l'améliorer...