OVH Cloud OVH Cloud

template friend operator

18 réponses
Avatar
meow
Bonjour,

=E7a a l'air d'etre un probl=E8me archi connu et rabach=E9, mais je n'ai
pas trouv=E9 la r=E9ponse ni sur la toile ni sur cette mailing
(probablement les mauvais mots clefs).

template <class T>
class A {
friend A<T> operator +<>(A<T>,A<T>);
};

template <class T>
A<T> operator+ (A<T>,A<T>){
}

error: template-id 'operator+<>' does not match any template
declaration

si je retire les vilains <> dans la d=E9claration friend operator :
warning: friend declaration declares a non-template function

(gcc 4.0.3 sous debian)

8 réponses

1 2
Avatar
Jean-Marc Bourguet
Fabien LE LEZ writes:

On Mon, 26 Nov 2007 05:34:43 -0800 (PST), meow :

(et du coups je me
demande meme pourquoi ces fonctions sont déclarées amies), sauf pour
certains comme ==, !=, << où des champs privés sont accédés :/


Pour == et !=, as-tu une raison particulière pour préférer une
fonction non-membre amie à une fonction membre ?


Comme toujours, l'assymétrie en présence de conversions implicites.

J'ai généralement un membre compare() et les opérateurs binaires de
comparaisons qui l'utilisent.

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
meow
Pour == et !=, as-tu une raison particulière pour préférer u ne
fonction non-membre amie à une fonction membre ?
Comme toujours, l'assymétrie en présence de conversions implicites.

J'ai généralement un membre compare() et les opérateurs binaires de
comparaisons qui l'utilisent.


Comme dit, je suis en train de tenter de faire fonctionner une vielle
bibal plus maintenue depuis un bon siècle, je n'ai donc aucune idée
concernant les choix d'implémentation qui ont été faits. Comme en
outre je ne suis pas moi meme un grand guru de la programmation j'ai
un peu peur de modifier trop de choses ;)
Maintenant, comme d'habiture, vos réponses me permettent en tout cas
d'avancer sur le plan de ma compréhension personnelle, meme s'il me
faut souvent courir derrière et googliser des bouts de vos réponses ;)

( Sinon, pour ce qui est du post en réponse à moi meme plutot qu'à
Kanze, c'est juste que je lis et interragis avec ce newsgroup via le
portail google et que j'en ai donc une vision complètement
séquentielle... J'imagine à ta question qu'il y a une structure
arborescente ? Désolé pour le dérangement. )


Avatar
Fabien LE LEZ
On Mon, 26 Nov 2007 07:18:59 -0800 (PST), meow
:

c'est juste que je lis et interragis avec ce newsgroup via le
portail google


N'as-tu pas la possibilité d'installer un vrai client NNTP ?

Avatar
meow
N'as-tu pas la possibilité d'installer un vrai client NNTP ?
Hum, ce sont de vieux souvenirs mais Je crois que j'avais essayé ça au

tout début, mais ici on a un firewall plutot drastique et comme je
n'avais pas trouvé de serveur de news sur le port 80 j'ai rapidement
opté pour la solution web qui marche pas trop mal en définitive :)

Avatar
James Kanze
On Nov 26, 11:09 am, meow wrote:
Bonjour, et merci pour vos réponses. Avant de continuer, je tiens à
préciser que mes soucis sont inhérents à la reprise d'un vieux code
(bibliothèque de minimisation coool). Pour info, le fichier dans
lequel j'ai expérimenté les soucis exposés ici date de 1994 Si ça peut
aider...


Aïe. Il faut dire que les règles en ce qui concerne les noms des
amis ont changé avec la norme, et qu'en 1994, il y a une forte
probabilité que le code a été écrit en vue des anciennes règles.
La plupart des cas, on finit avec le même résultat quand même,
quoique pour des raisons différentes, mais il y a certainement
des cas ou ce n'est pas vrai.

@Fabien, c'est essentiellement l'emploi du template qui complique
tout ;)

@Kanze:

Attention : ici, tu n'as pas déclaré une template comme ami,
mais une fonction non-template.


Euh, je t'avoues que je ne connais déjà pas cette notation avec les
chevrons vides :/

J'ai découvert récemment qu'on pouvait utiliser ce genre
d'écriture pour définir une implémentation particulière d'une
fonction ou d'une classe déclarée template et dont on souhaite
instancier les arguments template...


C'est un autre cas. Ça n'a rien à voir ici. Ici, il n'y a pas de
<>; seulement des <T>.

Selon la norme, je crois ce que tu veux, c'est :
friend A<T> operator+< A< T > >( A< T >, A< T > ) ;


J'ai essayé (dans mon cas A = Model et T=Type ), et gcc n'est toujou rs
pas content :
(Dans mon exemple j'ai
../../include/coool/Model.hh:226: error: template-id 'operator
+<Model<double> >' for 'Model<double> operator+(const Model<double>&,
const std::valarray<double>&)' does not match any template declaration


Bien, je ne suis pas vraiment sûr. Comme j'ai dit, c'est une
chose qui ne m'a jamais servi. L'écriture semble néanmoins
correspondre à l'exemple dans la norme :

template< class T > class task ;
template< class T > task<T>* preempt(task<T>*);
template< class T > class task {
// ...
friend task<T>* preempt<T>(task<T>*) ;
// ...
}

Et dans le texte : << because the friend preempt has an
explicit template-argument <T>, each specialization of the task
class template has the appropriate specialization of the
function template preempt as a friend >>.

Est-ce que tu es certain d'avoir déclaré le template de fonction
avant :

template< typename T > class Model ;
template< typename T > Model< T >
operator+( Model< T > const&, Model< T > const& ) ;

template< typename T >
class Model {
// ...
friend Model< T > operator+< T >( Model< T > const&,
Model< T > const& ) ;
// ...
} ;

(Je ne l'ai pas essayé, mais j'ai bien l'impression d'après la
norme que c'est ce qu'il faut. Et le message d'erreur semble
indiquer que le problème, c'est qu'il n'y a pas de déclaration
du template operator+ visible.)

Cela étant, je ne comprends pas ton écriture, et de plus je ne
vois pas ce que cela donnerait pour les cas où j'ai des
arguments d'autres types que Model<Type>:
friend Model<Type> operator+<>(const Model<Type>&, const
Model<Type>&);
friend Model<Type> operator+<>(const Model<Type>&, const
std::valarray<Type>&);
friend Model<Type> operator+<>(const std::valarray<Type>&,
const Model<Type>&);
friend Model<Type> operator*<>(Type, const Model<Type>&);


Si j'ai écrit ça, c'est une faute de frappe. Il faut bien mettre
le type dont l'instantation doit être l'ami dans les <>.

Et puis il ne faudrait pas spécifier explicitement qu'on a qu'il
s'agit d'une fonction template, un truc du genre :
template<>
friend Model<Type> operator+<Type>(const Model<Type>&, const
std::valarray<Type>&);
(j'ai l'impression d'etre un singe savant... Avec un peu de chance je
réécrirai proust avant d'avoir réussi à faire compiler/fonctionner ma
bibliothèque)

Seulement, dans ce cas-là, il faudrait une declaration du
template de la fonction avant la classe. Quelque chose du
genre :


En effet, c'est d'ailleurs le cas dans le fichier en question.
D'ailleurs, j'ai copié collé des sous parties un peu plus bas dans ce
message comme tu me l'avais demandé.

Pour le cas des operator
arithmetiques binaires, par exemple, je fais quelque chose du
genre :
Le template de classe ArithmeticOperators s'occupe de générer un
operator+ (en tant que fonction libre) à partir de l'operator+=.


Tu veux dire que si dans mon cas je commentes les déclarations
et définitions des fonctions libres, le compilo sera assez
aimable pour m'en fournir automatiquement a partir des
operateurs operation= (+=, *=, ...) de classe ?


Pas vraiment. Il existe une implémentation << standard >> des
opérateurs binaires +, -, etc., à partir des opérateurs +=, -=,
etc. Seulement, je ne veux pas cette implémentation dans un
template global, pour deux raisons : d'abord, je risque de la
prendre même quand il ne convient pas, et ensuite, si elle n'est
pas dans le bon espace référentiel, je risque de ne pas la
trouver.

Alors, je le mets dans une template de classe, qui, en tant que
classe de base, serait instantié automatiquement chaque fois que
la classe dérivée est instantiée. Et à cause de l'ADL, on va
chercher les noms déclarés dans les classes de bases chaque fois
que l'opérateur a comme paramètre la classe dérivée.

[...]
Est-ce que tu aurais un exemple du cas où tu as
l'avertissement ? Je m'y attendrais éventuellement pour ce que
tu as écrit ci-dessus.


ci dessous :

----------------------

//Model.hh
template <class Type>
class Model;
...
template <class Type>
Model<Type> operator+(const Model<Type>&, const Model<Type>&);
...
template <class Type>
class Model {
...
friend Model<Type> operator+<>(const Model<Type>&, const


C'est:
friend Model< Type > operator+< Type >(
Model< Type > const&, Model< Type > const& ) ;

qu'il faut ici.

--
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 Nov 26, 3:27 pm, meow wrote:
Oh, tant que j'y suis, je me rends compte qu'il y a deux petites
choses évoquées dans ce thread qui ne sont pas claires pour moi et que
je n'avais pas remarquées précédemment.

Dans le post de Kanze :

template< typename T >
class A : public ArithmeticOperators< A< T > >
{
public
A& operator+=( A const& other ) ;
// ...
} ;


tu voulais dire A<T>& operator +=(A<T> const& other) ? Ou bien ton
ecriture est elle aussi valide ?


À l'intérieur d'une classe templatée :

template< typename T >
class Toto { /* ... */ } ;

toute utilisation de Toto qui n'est pas suivi d'un < est traitée
comme s'il était suivi d'un <T>.

Et dans mon post, je me rends compte que je ne comprends pas le
fichier template.cc qui lève l'erreur :

#include "coool/Model.hh"
template class Model<double>;

C'est quoi cette utilisation du mot clef template ?


Une instantiation explicite. Elle provoque l'instantiation de
Model pour le type double.

Et concrètement, qu'est-ce qu'on fait là ? a cette ligne ? Une
tentative de deviner : on déclare l'existence d'une classe
Model<double>, et on force ce faisant l'instantiation du
template par le compilo ?


La plupart du temps, les instantiations explicites existent pour
s'assurer que une instantiation complète se trouve dans une
bibliothèque.

Ou plus loin

template double std::min(const DiagMatrix<double>&m);
la présence du std me trouble...


Moi aussi. Ici, il doit s'agir d'une instantiation explicite de
la fonction std::min, ou la déduction du type se fait à partir
des paramètres. Seulement, la fonction std::min prend deux
paramètres, et ici, je n'en vois qu'un.

j'ai bien trouvé une fonction libre min avec une signature
compatible dans le fichier DiagMatrix.cc, mais point de std...
Je suis dubitatif.


Moi aussi. Mais tu as déjà dit que le code est assez ancien. Il
ne faut pas oublier qu'en 1994, les templates étaient encore
dans un état assez flou, et que chaque implémentation faisait
quelque chose de différent. Alors, peut-être la ligne ci-dessus
avait un sens avec un compilateur donné, dans le temps.

--
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 Nov 26, 3:33 pm, Fabien LE LEZ wrote:
On Mon, 26 Nov 2007 05:34:43 -0800 (PST), meow :

(et du coups je me
demande meme pourquoi ces fonctions sont déclarées amies), sauf pour
certains comme ==, !=, << où des champs privés sont accédés :/


Pour == et !=, as-tu une raison particulière pour préférer une
fonction non-membre amie à une fonction membre ?


Si tu veux que les conversions s'effectuent de la même façon des
deux côtés de l'opérateur, il faut qu'il soit fonction libre.
(Si la classe ne supporte pas de conversions implicites, ce qui
est souvent le cas, alors le membre marche aussi bien.)

Si ça t'intéresse, j'utilise la même technique ici que pour les
opérateurs +, -, etc. Il y en a une version un peu ancienne dans
le fichier Operators.hh, à ma site, mais je viens de réécrire ce
fichier complètement : d'une part, je l'ai découpé dans
plusieurs fichiers : ArithmeticOperators.hh,
ComparisonOperators.hh, etc., et de l'autre, j'ai modifié les
ComparisonOperators pour utiliser de la méta-programmation pour
choisir la fonction membre à appeler : isEqual(), si elle existe,
compare() sinon. (J'ai aussi ajouté un cas :
STLIteratorOperators, qui génère ce qu'il faut pour faire un
itérateur STL à partir d'une itérateur normal.)

class A
{
public:
bool operator == (A const&) const;

};

Pour <<, généralement, on passe par une fonction membre
Print(ostream&).


C'est ce que je fais, mais je ne crois pas que ce soit une
politique très répandue (sauf, évidemment, quand il s'agit d'une
fonction virtuelle).

Toutefois, commence par te demander si tu as
réellement besoin d'accéder à des membres privés. En effet, << a p our
rôle d'afficher à l'écran (ou autre ostream) l'état observable de
l'objet ; logiquement, toutes les données qu'il affiche devrait être
accessibles (au moins en lecture) par du code extérieur (i.e. via
l'interface publique).


Tout dépend. Dans la plupart de mes applications, l'opérateur <<
ne sert que pour le log et le debugging. Dans ces cas-là,
l'accès à de l'état non visible peut se justifier. Mais en
général : la technique de Barton et Nackman que j'utilise ici
utilise des fonctions friend non pour accéder à des membres
privés (qui n'existe de toute façon pas dans la classe de base
où elles sont définies), mais parce que ça permet la définition
d'une fonction libre à l'intérieur d'une classe.

--
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
meow
Je suis un peu à la rue niveau travail, raison de mon long mutisme...
J'ai cependant pris la peine de lire attentivement toutes vos réponses
et je vous remercie encore une fois pour leur exhaustivité. Comme
d'habitude j'ai appris plus qu'attendu.

Par soucis de respect et d'exhaustivité :

- J'ai rapidement résolu le problème qui m'a fait ouvrir ce thread.
C'était juste une connerie :

---------------------------
template <class Type>
class Model;
...
template <class Type>
Model<Type> operator+(const Model<Type>&, const cooolvect<Type>&);
...
template <class Type>
class Model {
...
friend Model<Type> operator+<>(const Model<Type>&, const
Model<Type>&);
...
};
---------------------------
Les signatures des fonctions déclarées à l'extérieur puis dans la
classe ne se correspondaient pas O_O

- concernant les bibliothèques de minimisation numérique ou
d'optimisation, coool n'est plus maintenu depuis des lustres mais il y
en a quelques autres qui semblent l'etre, dont : Opt++, ou GSL
1 2