Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Yet another question sur friend et template...

15 réponses
Avatar
Jaco
Bonjour,

Moi aussi je me remet doucement =E0 C++... j'avais d=E9j=E0 un peu de mal =
=E0
l'=E9poque avec les templates et les friends et =E7a ne s'est pas
arrang=E9 !

Si je prend ce programme "jouet" suivant :

#include <iostream>
using namespace std;

const int TailleDefaut =3D 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 =3D TailleDefaut);
Array(const Array &rhs);
~Array() { delete [] pType; }

Array& operator=3D(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=E9mentations...

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

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

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

template <class T>
ostream& operator<<(ostream& sortie, Array<T>& unTableau) {
for (int i =3D 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=EAter) : ";
cin >> indice >> valeur;

if (indice < 0)
break;

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

leTableau[indice] =3D valeur;
}

cout << "\nVoici le tableau complet :\n";
cout << leTableau << endl;
return 0;
}

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

% 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=E9claration de friend... mais j'avoue que je s=E8che... Une piste ?

Cordialement

10 réponses

1 2
Avatar
Fabien LE LEZ
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é.)
Avatar
Jaco
On 22 mar, 01:46, Fabien LE LEZ wrote:

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...
Avatar
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?

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

--
Merwin
Avatar
Jaco
On 22 mar, 09:35, Jaco wrote:

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.
Avatar
James Kanze
On Mar 22, 1:23 am, Jaco wrote:

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
Avatar
James Kanze
On Mar 22, 10:08 am, Merwin wrote:
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
Avatar
Merwin
James Kanze a écrit :
On Mar 22, 10:08 am, Merwin wrote:
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?
Avatar
Eric Jacoboni
In article (Dans l'article)
,
James Kanze wrote (écrivait) :


> 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...
Avatar
Eric Jacoboni
In article (Dans l'article) <49c62bee$0$2733$,
Merwin wrote (écrivait) :


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 :)
Avatar
Merwin
Eric Jacoboni a écrit :
In article (Dans l'article) <49c62bee$0$2733$,
Merwin wrote (écrivait) :


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.
1 2