Yet another question sur friend et template...

Le
Jaco
Bonjour,

Moi aussi je me remet doucement à C++ j'avais déjà un peu de mal =
à
l'époque avec les templates et les friends et ça ne s'est pas
arrangé !

Si je prend ce programme "jouet" suivant :

#include <iostream>
using namespace std;

const int TailleDefaut = 10;

class Animal {
public:
Animal(int);
Animal();
~Animal() {}
int GetPoids() const { return sonPoids; }
void Affiche() const { cout << sonPoids; }
private:
int sonPoids;
};

Animal::Animal(int poids): sonPoids(poids) {}

Animal::Animal(): sonPoids(0) {}

template <class T>
class Array {
public:
Array(int taille = TailleDefaut);
Array(const Array &rhs);
~Array() { delete [] pType; }

Array& operator=(const Array&);
T& operator[](int indice) { return pType[indice]; }
const T& operator[](int indice) const { return pType
[indice]; }

int GetTaille() const { return saTaille; }
friend ostream& operator<<(ostream&, Array<T>&);

private:
T *pType;
int saTaille;
};

// Implémentations

template <class T>
Array<T>::Array(int taille): saTaille(taille) {
pType = new T[taille];
for (int i = 0; i < taille; i++)
pType[i] = 0;
}

template <class T>
Array<T>::Array(const Array &rhs) {
saTaille = rhs.GetTaille();
pType = new T[saTaille];
for (int i = 0; i < saTaille; i++)
pType[i] = rhs[i];
}

template <class T>
Array<T>& Array<T>::operator=(const Array &rhs) {
if (this == &rhs)
return *this;
delete [] pType;
saTaille = rhs.GetTaille();
pType = new T[saTaille];
for (int i = 0; i < saTaille; i++)
pType[i] = rhs[i];
return *this;
}

template <class T>
ostream& operator<<(ostream& sortie, Array<T>& unTableau) {
for (int i = 0; i < unTableau.saTaille; i++)
sortie << "[" << i << "] " << unTableau[i] << endl;
return sortie;
}

int main() {
int indice, valeur;
Array<int> leTableau ;

while (true) {
cout << "Entrez un indice (0-9) ";
cout << "et une valeur. (-1 pour arrêter) : ";
cin >> indice >> valeur;

if (indice < 0)
break;

if (indice > 9) {
cout << "Entrez une valeur entre 0 et 9.";
continue;
}

leTableau[indice] = valeur;
}

cout << "Voici le tableau complet :";
cout << leTableau << endl;
return 0;
}

Et que je tente de le compiler avec g++-4.0, j'obtiens ça :

% g++ listing19_4.cpp
listing19_4.cpp:33: warning: friend declaration 'std::ostream&
operator<<(std::ostream&, Array<T>&)' declares a non-template function
listing19_4.cpp:33: warning: (if this is not what you intended, make
sure the function template has already been declared and add <> after
the function name here) -Wno-non-template-friend disables this warning
Undefined symbols:
"operator<<(std::basic_ostream<char, std::char_traits<char> >&,
Array<int>&)", referenced from:
_main in cciJyZWs.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
%

Le message du compilo m'indique bien qu'il y a un pb avec ma
déclaration de friend mais j'avoue que je sèche Une piste ?

Cordialement
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 2
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Fabien LE LEZ
Le #18953321
On Sat, 21 Mar 2009 17:23:06 -0700 (PDT), Jaco

Le message du compilo m'indique bien qu'il y a un pb avec ma
déclaration de friend...



Et t'indique même le problème en question.

Suivre à la lettre les indications données par g++ résoud le souci.

mais j'avoue que je sèche... Une piste ?



Éviter l'usage de "friend" ?
(Ce n'est pas du sarcasme : il est très très rare que j'utilise ce
mot-clé.)
Jaco
Le #18953831
On 22 mar, 01:46, Fabien LE LEZ
Suivre à la lettre les indications données par g++ résoud le souci.



Ben, c'est ce que j'ai commencé par faire, évidemment, sinon je
n'aurai pas de souci... :)
Donc soit je ne ne comprend pas les indications, soit je ne comprend
pas les indications...

Éviter l'usage de "friend" ?



Oui, mais là c'est un exercice d'un bouquin...
Merwin
Le #18953961
Jaco a écrit :

[snip]
int GetPoids() const { return sonPoids; }
void Affiche() const { cout << sonPoids; }


[snip]

Petite question hors sujet, est-ce mieux de déclarer ce genre
d'accesseurs 'inline' ou ça n'a aucun intéret?

Pour tout soucis je n'ai pas le niveau désolé :-)

--
Merwin
Jaco
Le #18954351
On 22 mar, 09:35, Jaco
Donc soit je ne ne comprend pas les indications, soit je ne comprend
pas les indications...



Bon, en désespoir de cause, j'ai remplacé la déclaration de la
fonction amie par :

template <class Q>
friend ostream& operator<<(ostream&, Array<Q>&);

et c'est mieux... mais bon, le deuxième paramètre n'est pas "corrél é"
au T de la classe englobante, ce qui était (je pense) l'idée première
de l'exercice.
James Kanze
Le #18954671
On Mar 22, 1:23 am, Jaco
Moi aussi je me remet doucement à C++... j'avais déjà un peu
de mal à l'époque avec les templates et les friends et ça ne
s'est pas arrangé !



Si je prend ce programme "jouet" suivant :



#include <iostream>
using namespace std;



const int TailleDefaut = 10;



class Animal {



Juste curieux, mais qu'est-ce que cette classe a à faire avec la
reste du programme ?

[...]
};



[...]

template <class T>
class Array {
public:


[...]
friend ostream& operator<<(ostream&, Array<T>&);


[...]
};



[...]

template <class T>
ostream& operator<<(ostream& sortie, Array<T>& unTableau) {
for (int i = 0; i < unTableau.saTaille; i++)
sortie << "[" << i << "] " << unTableau[i] << endl;
return sortie;
}



Et que je tente de le compiler avec g++-4.0, j'obtiens ça :



% g++ listing19_4.cpp
listing19_4.cpp:33: warning: friend declaration 'std::ostream&
operator<<(std::ostream&, Array<T>&)' declares a non-template function
listing19_4.cpp:33: warning: (if this is not what you intended, make
sure the function template has already been declared and add <> after
the function name here) -Wno-non-template-friend disables this warning
Undefined symbols:
"operator<<(std::basic_ostream<char, std::char_traits<char> >&,
Array<int>&)", referenced from:
_main in cciJyZWs.o
ld: symbol(s) not found
collect2: ld returned 1 exit status
%



Le message du compilo m'indique bien qu'il y a un pb avec ma
déclaration de friend... mais j'avoue que je sèche... Une
piste ?



Pour une fois, le message est vraiment parlant. Ta déclaration
de friend déclare une fonction non-templatée comme amie ; une
différente function, évidemment, pour chaque spécialisation.
Pour déclarer la spécialisation de la fonction templatée, il
faut :
friend ostream& operator<<<T>( ostream&, Array<T>& ) ;
Seulement, pour que ceci soit légal, il faut qu'il y a déjà une
déclaration du template de fonction visible. Quelque chose du
genre :
template< typename T > class Array ;
template< typename T >
ostream& operator<<( ostream&, Array< T > const& ) ;
avant la définition de la classe.

À mon avis, il est plus simplement de laisser l'ami
non-template. Ça veut dire qu'il faut fournir une définition de
la fonction pour chaque spécialisation de la classe, ce qui ne
peut se faire de façon pratique que si la fonction est inline,
e.g.:

friend ostream& operator<<(
ostream& dest,
Array<T>& obj )
{
obj.print( dest ) ;
return dest ;
}

Avec, évidemment, toute la vraie implémentation dans
Array<>::print. (On remarquera que dans ce cas-ci, on cesse
d'avoir besoin de l'amité pour des raisons d'accès. En revanche,
c'est le seul moyen qui permet à la définition d'une fonction
non-membre dans la classe même.)

Dans la pratique, j'ai ces fonctions (operator<< et operator>>)
dans un template de classe de base, IOStreamOperators dont
j'hérite :

template< typename T >
class Array : private IOStreamOperators< Array< T > > ...

Du coup, je n'encombre pas la définition d'Array avec ces
fonctions inline.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
James Kanze
Le #18954661
On Mar 22, 10:08 am, Merwin
Jaco a écrit :



[snip]> int GetPoids() const { return sonPoids; }
> void Affiche() const { cout << sonPoids; }



[snip]



Petite question hors sujet, est-ce mieux de déclarer ce genre
d'accesseurs 'inline' ou ça n'a aucun intéret?



Toute fonction définie dans la classe ou dans le template de
classe est inline.

Comme il a dit, c'est un exércise d'apprentissage. Dans ce
cas-là, c'est probablement préférable, de façon à éviter des
fichiers supplémentaires, et de pouvoir se concentrer sur
l'essentiel de ce qu'on essaie d'apprendre. Dans le code de
production, la règle générale est de ne définir aucune fonction
dans la classe même, afin de ne pas l'encombrer, et de ne pas
rendre la lecture plus difficile (mais il peut y avoir des
exceptions dans des cas précis). Aussi, en général, on évite des
fonctions inline dans tout ce qui n'est pas templates, à cause
de l'augmentation du couplage qui en résulte.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Merwin
Le #18954891
James Kanze a écrit :
On Mar 22, 10:08 am, Merwin
Jaco a écrit :



[snip]> int GetPoids() const { return sonPoids; }
void Affiche() const { cout << sonPoids; }





[snip]



Petite question hors sujet, est-ce mieux de déclarer ce genre
d'accesseurs 'inline' ou ça n'a aucun intéret?



Toute fonction définie dans la classe ou dans le template de
classe est inline.

Comme il a dit, c'est un exércise d'apprentissage. Dans ce
cas-là, c'est probablement préférable, de façon à éviter des
fichiers supplémentaires, et de pouvoir se concentrer sur
l'essentiel de ce qu'on essaie d'apprendre. Dans le code de
production, la règle générale est de ne définir aucune fonction
dans la classe même, afin de ne pas l'encombrer, et de ne pas
rendre la lecture plus difficile (mais il peut y avoir des
exceptions dans des cas précis). Aussi, en général, on évite des
fonctions inline dans tout ce qui n'est pas templates, à cause
de l'augmentation du couplage qui en résulte.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Au niveau du temps d'éxécution, les fonctions inline ne sont elles pas
plus rapide?
Eric Jacoboni
Le #18954881
In article (Dans l'article)
James Kanze

> class Animal {

Juste curieux, mais qu'est-ce que cette classe a à faire avec la
reste du programme ?



Oups !!! Bonne remarque... J'étais tellement obnubilé par cette
histoire de friend/template que je n'ai même pas vu que cette classe
n'était pas utilisée par le programme :)

Pour le reste, merci pour les explications...
Eric Jacoboni
Le #18955001
In article (Dans l'article) Merwin

Au niveau du temps d'éxécution, les fonctions inline ne sont elles pas
plus rapide?



Mes souvenirs me rappellent que 'inline' est, de toutes façons, une
supplique au compilateur qui en fera ce que bon lui semble... et que,
mal employé, ça pouvait faire plus de dégâts qu'autre chose.

Après, c'est sûr que pour une fonction d'une seule ligne comme ici, si
l'appel de la méthode est remplacé directement par son code, on y gagne
un appel... Mais, bon en l'occurrence ici, je pense que c'était plus
pour montrer que pour optimiser :)
Merwin
Le #18955201
Eric Jacoboni a écrit :
In article (Dans l'article) Merwin

Au niveau du temps d'éxécution, les fonctions inline ne sont elles pas
plus rapide?



Mes souvenirs me rappellent que 'inline' est, de toutes façons, une
supplique au compilateur qui en fera ce que bon lui semble... et que,
mal employé, ça pouvait faire plus de dégâts qu'autre chose.

Après, c'est sûr que pour une fonction d'une seule ligne comme ici, si
l'appel de la méthode est remplacé directement par son code, on y gagne
un appel... Mais, bon en l'occurrence ici, je pense que c'était plus
pour montrer que pour optimiser :)



Oui oui, je ne parle pas de ce cas précis. Mais en cours il me semblait
qu'on mavais dit, comme tu l'as rappelé, que les fonctions inline
évitaient un appel, d'où leurs intéréts.

Je voulais une confirmation, pour savoir si je pouvais continuer à les
utiliser pour mes accesseurs ou si c'était une habitude à perdre.
Publicité
Poster une réponse
Anonyme