J'ai un soucis de comprehension sur une classe qui possede
un operateur de conversion automatique (j'aurais du me mefier,
on m'avais prevenu, mais je l'ai fait quand meme...)
Donc, soit la classe en question, reduite a sa plus
simple expression :
template< typename T >
class Wrapper
{
public:
explicit Wrapper( T const& value = T() ) : myValue( value ) {}
operator T () const { return myValue ; }
private:
T myValue ;
} ;
Dans le code, on avait ceci :
Wrapper< int > x ;
if ( x == 0 ) {
// ...
}
Pour le test d'egalite, il y a conversion automatique
vers int puis appel de l'operateur == sur int, ca compile,
ca marche, tout va bien.
Plus tard, on a changé le code, et remplace int par std::string.
Et la, c'est le drame : le compilateur refuse le test d'egalite,
comme s'il ne trouvait pas l'operateur de conversion automatique :
Wrapper< std::string > x ;
if ( x == std::string( "" ) ) {
// ...
}
erreur : no match for 'operator==' in 'x == y'
(testé avec gcc-4.1.2, xlC-8, Comeau 4.3.10.1).
Pour essayer de comprendre, j'ai remplacé std::string par une
classe vide, et la ca compile :
Par contre, si la classe encapsulee est template, alors
ca ne compile plus :
template < typename T >
struct Generic {} ;
template < typename T >
bool operator==( Generic< T > const&, Generic< T > const& )
{ return true ; }
Wrapper< Generic< int > > x ;
if ( x == Generic< int>() ) ; // KO : no match for 'operator=='
Donc, il semblerait que dans une expression d'egalité, l'operateur
de conversion automatique de Wrapper< T > n'est pas trouvé lorsque
T est lui-meme un type template.
Est-ce qu'il y a une raison qui explique ce comportement ?
Cette action est irreversible, confirmez la suppression du commentaire ?
Signaler le commentaire
Veuillez sélectionner un problème
Nudité
Violence
Harcèlement
Fraude
Vente illégale
Discours haineux
Terrorisme
Autre
Sylvain SF
Michel Decima a écrit :
Bonjour,
J'ai un soucis de comprehension sur une classe qui possede un operateur de conversion automatique (j'aurais du me mefier, on m'avais prevenu, mais je l'ai fait quand meme...)
mais non, c'est utile.
Donc, soit la classe en question, reduite a sa plus simple expression :
template< typename T > class Wrapper { public: explicit Wrapper( T const& value = T() ) : myValue( value ) {} operator T () const { return myValue ; } private: T myValue ; } ;
Plus tard, on a changé le code, et remplace int par std::string. Et la, c'est le drame : le compilateur refuse le test d'egalite, comme s'il ne trouvait pas l'operateur de conversion automatique
j'aurais mis un operator (paramétré) de test dans le template:
J'ai un soucis de comprehension sur une classe qui possede
un operateur de conversion automatique (j'aurais du me mefier,
on m'avais prevenu, mais je l'ai fait quand meme...)
mais non, c'est utile.
Donc, soit la classe en question, reduite a sa plus
simple expression :
template< typename T >
class Wrapper {
public:
explicit Wrapper( T const& value = T() ) : myValue( value ) {}
operator T () const { return myValue ; }
private:
T myValue ;
} ;
Plus tard, on a changé le code, et remplace int par std::string.
Et la, c'est le drame : le compilateur refuse le test d'egalite,
comme s'il ne trouvait pas l'operateur de conversion automatique
j'aurais mis un operator (paramétré) de test dans le template:
J'ai un soucis de comprehension sur une classe qui possede un operateur de conversion automatique (j'aurais du me mefier, on m'avais prevenu, mais je l'ai fait quand meme...)
mais non, c'est utile.
Donc, soit la classe en question, reduite a sa plus simple expression :
template< typename T > class Wrapper { public: explicit Wrapper( T const& value = T() ) : myValue( value ) {} operator T () const { return myValue ; } private: T myValue ; } ;
Plus tard, on a changé le code, et remplace int par std::string. Et la, c'est le drame : le compilateur refuse le test d'egalite, comme s'il ne trouvait pas l'operateur de conversion automatique
j'aurais mis un operator (paramétré) de test dans le template:
On Wed, 27 May 2009 18:24:25 +0200, Michel Decima :
template < typename T > struct Generic {} ;
template < typename T > bool operator==( Generic< T > const&, Generic< T > const& ) { return true ; } Wrapper< Generic< int > > x ; if ( x == Generic< int>() ) ; // KO : no match for 'operator=='
Simplifions un peu :
template < typename T > struct Generic {} ;
template < typename T > void f (Generic< T > const&) {}
Wrapper< Generic< int > > x ; f (x);
f<>() est une fonction template. Le compilateur peut trouver (inférer) lui-même le type paramètre template, à condition qu'aucune conversion implicite ne soit nécessaire. En d'autres termes, il faudrait qu'il existe un type T pour lequel x soit de type Generic<T>. Ce n'est pas possible, donc le compilo ne sait pas trouver T tout seul.
En revanche, tu peux indiquer le type explicitement :
Wrapper< Generic< int > > x ; f<int> (x);
f<int>() est une fonction normale, qui accepte comme paramètre un "Generic<int> const&", et x est convertible en ce type, donc tout va bien.
Évidemment, avec operator== et basic_string<plein_de_trucs>, expliciter les paramètres templates s'avère plus délicat :-(
On Wed, 27 May 2009 18:24:25 +0200, Michel Decima :
template < typename T >
struct Generic {} ;
template < typename T >
bool operator==( Generic< T > const&, Generic< T > const& )
{ return true ; }
Wrapper< Generic< int > > x ;
if ( x == Generic< int>() ) ; // KO : no match for 'operator=='
Simplifions un peu :
template < typename T > struct Generic {} ;
template < typename T >
void f (Generic< T > const&) {}
Wrapper< Generic< int > > x ;
f (x);
f<>() est une fonction template.
Le compilateur peut trouver (inférer) lui-même le type paramètre
template, à condition qu'aucune conversion implicite ne soit
nécessaire.
En d'autres termes, il faudrait qu'il existe un type T pour lequel x
soit de type Generic<T>. Ce n'est pas possible, donc le compilo ne
sait pas trouver T tout seul.
En revanche, tu peux indiquer le type explicitement :
Wrapper< Generic< int > > x ;
f<int> (x);
f<int>() est une fonction normale, qui accepte comme paramètre un
"Generic<int> const&", et x est convertible en ce type, donc tout va
bien.
Évidemment, avec operator== et basic_string<plein_de_trucs>,
expliciter les paramètres templates s'avère plus délicat :-(
On Wed, 27 May 2009 18:24:25 +0200, Michel Decima :
template < typename T > struct Generic {} ;
template < typename T > bool operator==( Generic< T > const&, Generic< T > const& ) { return true ; } Wrapper< Generic< int > > x ; if ( x == Generic< int>() ) ; // KO : no match for 'operator=='
Simplifions un peu :
template < typename T > struct Generic {} ;
template < typename T > void f (Generic< T > const&) {}
Wrapper< Generic< int > > x ; f (x);
f<>() est une fonction template. Le compilateur peut trouver (inférer) lui-même le type paramètre template, à condition qu'aucune conversion implicite ne soit nécessaire. En d'autres termes, il faudrait qu'il existe un type T pour lequel x soit de type Generic<T>. Ce n'est pas possible, donc le compilo ne sait pas trouver T tout seul.
En revanche, tu peux indiquer le type explicitement :
Wrapper< Generic< int > > x ; f<int> (x);
f<int>() est une fonction normale, qui accepte comme paramètre un "Generic<int> const&", et x est convertible en ce type, donc tout va bien.
Évidemment, avec operator== et basic_string<plein_de_trucs>, expliciter les paramètres templates s'avère plus délicat :-(
James Kanze
On May 27, 6:24 pm, Michel Decima wrote:
J'ai un soucis de comprehension sur une classe qui possede un operateur de conversion automatique (j'aurais du me mefier, on m'avais prevenu, mais je l'ai fait quand meme...)
Donc, soit la classe en question, reduite a sa plus simple expression :
template< typename T > class Wrapper { public: explicit Wrapper( T const& value = T() ) : myValue( value ) {} operator T () const { return myValue ; } private: T myValue ; } ;
Dans le code, on avait ceci :
Wrapper< int > x ; if ( x == 0 ) { // ... }
Pour le test d'egalite, il y a conversion automatique vers int puis appel de l'operateur == sur int, ca compile, ca marche, tout va bien.
Plus tard, on a changé le code, et remplace int par std::string. Et la, c'est le drame : le compilateur refuse le test d'egalite, comme s'il ne trouvait pas l'operateur de conversion automatique :
Wrapper< std::string > x ; if ( x == std::string( "" ) ) { // ... }
erreur : no match for 'operator==' in 'x == y'
Ce n'est pas la conversion implicite qu'il ne trouve pas. C'est opérateur == tout court. Les seuls opérateurs == dont il peut être question, ce sont ceux dans <string>. Sauf qu'ils ne sont pas des fonctions, mais des templates, et ne peuvent être pris en compte qu'une fois instantiées. Et la déduction automatique des types, qui permettrait l'instantiation, ne prend pas en compte les conversions implicites (ou seulement quelques conversions très simples, du genre tableau en pointeur).
(testé avec gcc-4.1.2, xlC-8, Comeau 4.3.10.1).
Pour essayer de comprendre, j'ai remplacé std::string par une classe vide, et la ca compile :
Par contre, si la classe encapsulee est template, alors ca ne compile plus :
template < typename T > struct Generic {} ; template < typename T > bool operator==( Generic< T > const&, Generic< T > const& ) { return true ; }
Wrapper< Generic< int > > x ; if ( x == Generic< int>() ) ; // KO : no match for 'operator= ='
Donc, il semblerait que dans une expression d'egalité, l'operateur de conversion automatique de Wrapper< T > n'est pas trouvé lorsque T est lui-meme un type template.
La différence, ce n'est pas T, c'est l'operator==. Si l'operator== est une fonction templatée, la déduction de type, ne prenant pas en compte les conversions implicites, échouera, et il n'y aura pas de fonction à ajouter à l'ensemble de surcharge.
La solution évidente, c'est de ne jamais faire operator== un template. Faute d'autres solutions :
template< typename T > class Generic { public: friend bool operator==( Generic const& lhs, Generic const& rhs ) { return true /* ou d'autre chose */ ; } } ;
(Du coup, l'operator== n'est pas un template. Mais il y en a autant qu'il faut.)
Pour std::string, évidement, il n'y a rien que tu puisses faire. (Une question intéressante : est-ce qu'une implémentation de std::basic_string a le droit de faire comme ci-dessus ?)
-- 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
On May 27, 6:24 pm, Michel Decima <michel.dec...@orange-ft.com> wrote:
J'ai un soucis de comprehension sur une classe qui possede un
operateur de conversion automatique (j'aurais du me mefier, on
m'avais prevenu, mais je l'ai fait quand meme...)
Donc, soit la classe en question, reduite a sa plus
simple expression :
template< typename T >
class Wrapper
{
public:
explicit Wrapper( T const& value = T() ) : myValue( value ) {}
operator T () const { return myValue ; }
private:
T myValue ;
} ;
Dans le code, on avait ceci :
Wrapper< int > x ;
if ( x == 0 ) {
// ...
}
Pour le test d'egalite, il y a conversion automatique vers int
puis appel de l'operateur == sur int, ca compile, ca marche,
tout va bien.
Plus tard, on a changé le code, et remplace int par
std::string. Et la, c'est le drame : le compilateur refuse le
test d'egalite, comme s'il ne trouvait pas l'operateur de
conversion automatique :
Wrapper< std::string > x ;
if ( x == std::string( "" ) ) {
// ...
}
erreur : no match for 'operator==' in 'x == y'
Ce n'est pas la conversion implicite qu'il ne trouve pas. C'est
opérateur == tout court. Les seuls opérateurs == dont il peut
être question, ce sont ceux dans <string>. Sauf qu'ils ne sont
pas des fonctions, mais des templates, et ne peuvent être pris
en compte qu'une fois instantiées. Et la déduction automatique
des types, qui permettrait l'instantiation, ne prend pas en
compte les conversions implicites (ou seulement quelques
conversions très simples, du genre tableau en pointeur).
(testé avec gcc-4.1.2, xlC-8, Comeau 4.3.10.1).
Pour essayer de comprendre, j'ai remplacé std::string par une
classe vide, et la ca compile :
Par contre, si la classe encapsulee est template, alors
ca ne compile plus :
template < typename T >
struct Generic {} ;
template < typename T >
bool operator==( Generic< T > const&, Generic< T > const& )
{ return true ; }
Wrapper< Generic< int > > x ;
if ( x == Generic< int>() ) ; // KO : no match for 'operator= ='
Donc, il semblerait que dans une expression d'egalité,
l'operateur de conversion automatique de Wrapper< T > n'est
pas trouvé lorsque T est lui-meme un type template.
La différence, ce n'est pas T, c'est l'operator==. Si
l'operator== est une fonction templatée, la déduction de type,
ne prenant pas en compte les conversions implicites, échouera,
et il n'y aura pas de fonction à ajouter à l'ensemble de
surcharge.
La solution évidente, c'est de ne jamais faire operator== un
template. Faute d'autres solutions :
template< typename T >
class Generic
{
public:
friend bool operator==( Generic const& lhs, Generic const&
rhs )
{
return true /* ou d'autre chose */ ;
}
} ;
(Du coup, l'operator== n'est pas un template. Mais il y en a
autant qu'il faut.)
Pour std::string, évidement, il n'y a rien que tu puisses faire.
(Une question intéressante : est-ce qu'une implémentation de
std::basic_string a le droit de faire comme ci-dessus ?)
--
James Kanze (GABI Software) email:james.kanze@gmail.com
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
J'ai un soucis de comprehension sur une classe qui possede un operateur de conversion automatique (j'aurais du me mefier, on m'avais prevenu, mais je l'ai fait quand meme...)
Donc, soit la classe en question, reduite a sa plus simple expression :
template< typename T > class Wrapper { public: explicit Wrapper( T const& value = T() ) : myValue( value ) {} operator T () const { return myValue ; } private: T myValue ; } ;
Dans le code, on avait ceci :
Wrapper< int > x ; if ( x == 0 ) { // ... }
Pour le test d'egalite, il y a conversion automatique vers int puis appel de l'operateur == sur int, ca compile, ca marche, tout va bien.
Plus tard, on a changé le code, et remplace int par std::string. Et la, c'est le drame : le compilateur refuse le test d'egalite, comme s'il ne trouvait pas l'operateur de conversion automatique :
Wrapper< std::string > x ; if ( x == std::string( "" ) ) { // ... }
erreur : no match for 'operator==' in 'x == y'
Ce n'est pas la conversion implicite qu'il ne trouve pas. C'est opérateur == tout court. Les seuls opérateurs == dont il peut être question, ce sont ceux dans <string>. Sauf qu'ils ne sont pas des fonctions, mais des templates, et ne peuvent être pris en compte qu'une fois instantiées. Et la déduction automatique des types, qui permettrait l'instantiation, ne prend pas en compte les conversions implicites (ou seulement quelques conversions très simples, du genre tableau en pointeur).
(testé avec gcc-4.1.2, xlC-8, Comeau 4.3.10.1).
Pour essayer de comprendre, j'ai remplacé std::string par une classe vide, et la ca compile :
Par contre, si la classe encapsulee est template, alors ca ne compile plus :
template < typename T > struct Generic {} ; template < typename T > bool operator==( Generic< T > const&, Generic< T > const& ) { return true ; }
Wrapper< Generic< int > > x ; if ( x == Generic< int>() ) ; // KO : no match for 'operator= ='
Donc, il semblerait que dans une expression d'egalité, l'operateur de conversion automatique de Wrapper< T > n'est pas trouvé lorsque T est lui-meme un type template.
La différence, ce n'est pas T, c'est l'operator==. Si l'operator== est une fonction templatée, la déduction de type, ne prenant pas en compte les conversions implicites, échouera, et il n'y aura pas de fonction à ajouter à l'ensemble de surcharge.
La solution évidente, c'est de ne jamais faire operator== un template. Faute d'autres solutions :
template< typename T > class Generic { public: friend bool operator==( Generic const& lhs, Generic const& rhs ) { return true /* ou d'autre chose */ ; } } ;
(Du coup, l'operator== n'est pas un template. Mais il y en a autant qu'il faut.)
Pour std::string, évidement, il n'y a rien que tu puisses faire. (Une question intéressante : est-ce qu'une implémentation de std::basic_string a le droit de faire comme ci-dessus ?)
-- 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
Michel Decima
Fabien LE LEZ a écrit :
On Wed, 27 May 2009 18:24:25 +0200, Michel Decima :
Simplifions un peu :
template < typename T > struct Generic {} ;
template < typename T > void f (Generic< T > const&) {}
Wrapper< Generic< int > > x ; f (x);
f<>() est une fonction template. Le compilateur peut trouver (inférer) lui-même le type paramètre template, à condition qu'aucune conversion implicite ne soit nécessaire.
Ok, c'est l'explication que je cherchais : les conversions implicites ne sont pas prises en compte dans l'inference des parametres templates.
En revanche, tu peux indiquer le type explicitement :
Wrapper< Generic< int > > x ; f<int> (x);
f<int>() est une fonction normale, qui accepte comme paramètre un "Generic<int> const&", et x est convertible en ce type, donc tout va bien.
Oui, mais j'aurais voulu eviter d'indiquer explicitement le type...
Évidemment, avec operator== et basic_string<plein_de_trucs>, expliciter les paramètres templates s'avère plus délicat :-(
Ben voila... c'est tout le probleme. Merci pour l'explication.
Fabien LE LEZ a écrit :
On Wed, 27 May 2009 18:24:25 +0200, Michel Decima :
Simplifions un peu :
template < typename T > struct Generic {} ;
template < typename T >
void f (Generic< T > const&) {}
Wrapper< Generic< int > > x ;
f (x);
f<>() est une fonction template.
Le compilateur peut trouver (inférer) lui-même le type paramètre
template, à condition qu'aucune conversion implicite ne soit
nécessaire.
Ok, c'est l'explication que je cherchais : les conversions
implicites ne sont pas prises en compte dans l'inference
des parametres templates.
En revanche, tu peux indiquer le type explicitement :
Wrapper< Generic< int > > x ;
f<int> (x);
f<int>() est une fonction normale, qui accepte comme paramètre un
"Generic<int> const&", et x est convertible en ce type, donc tout va
bien.
Oui, mais j'aurais voulu eviter d'indiquer explicitement le type...
Évidemment, avec operator== et basic_string<plein_de_trucs>,
expliciter les paramètres templates s'avère plus délicat :-(
Ben voila... c'est tout le probleme.
Merci pour l'explication.
On Wed, 27 May 2009 18:24:25 +0200, Michel Decima :
Simplifions un peu :
template < typename T > struct Generic {} ;
template < typename T > void f (Generic< T > const&) {}
Wrapper< Generic< int > > x ; f (x);
f<>() est une fonction template. Le compilateur peut trouver (inférer) lui-même le type paramètre template, à condition qu'aucune conversion implicite ne soit nécessaire.
Ok, c'est l'explication que je cherchais : les conversions implicites ne sont pas prises en compte dans l'inference des parametres templates.
En revanche, tu peux indiquer le type explicitement :
Wrapper< Generic< int > > x ; f<int> (x);
f<int>() est une fonction normale, qui accepte comme paramètre un "Generic<int> const&", et x est convertible en ce type, donc tout va bien.
Oui, mais j'aurais voulu eviter d'indiquer explicitement le type...
Évidemment, avec operator== et basic_string<plein_de_trucs>, expliciter les paramètres templates s'avère plus délicat :-(
Ben voila... c'est tout le probleme. Merci pour l'explication.
Michel Decima
Sylvain SF a écrit :
Michel Decima a écrit :
Bonjour,
J'ai un soucis de comprehension sur une classe qui possede un operateur de conversion automatique (j'aurais du me mefier, on m'avais prevenu, mais je l'ai fait quand meme...)
mais non, c'est utile.
Je ne nie pas l'utilité. Mais il y a quand meme pas mal d'inconvénients, certains sont bien décrits dans la litterature, et je n'avais encore jamais lu quoi que ce soit sur celui que je viens de trouver (ou alors, je n'avait pas fait le rapprochement). Bref, c'est casse pied.
j'aurais mis un operator (paramétré) de test dans le template:
Oui, c'est surement comme ca que ca va finir, mais je voulais avoir une explication sur le 'pourquoi' de l'erreur produite par le compilateur.
Sylvain SF a écrit :
Michel Decima a écrit :
Bonjour,
J'ai un soucis de comprehension sur une classe qui possede
un operateur de conversion automatique (j'aurais du me mefier,
on m'avais prevenu, mais je l'ai fait quand meme...)
mais non, c'est utile.
Je ne nie pas l'utilité. Mais il y a quand meme pas mal
d'inconvénients, certains sont bien décrits dans la
litterature, et je n'avais encore jamais lu quoi que
ce soit sur celui que je viens de trouver (ou alors,
je n'avait pas fait le rapprochement). Bref, c'est
casse pied.
j'aurais mis un operator (paramétré) de test dans le template:
J'ai un soucis de comprehension sur une classe qui possede un operateur de conversion automatique (j'aurais du me mefier, on m'avais prevenu, mais je l'ai fait quand meme...)
mais non, c'est utile.
Je ne nie pas l'utilité. Mais il y a quand meme pas mal d'inconvénients, certains sont bien décrits dans la litterature, et je n'avais encore jamais lu quoi que ce soit sur celui que je viens de trouver (ou alors, je n'avait pas fait le rapprochement). Bref, c'est casse pied.
j'aurais mis un operator (paramétré) de test dans le template:
Oui, c'est surement comme ca que ca va finir, mais je voulais avoir une explication sur le 'pourquoi' de l'erreur produite par le compilateur.
Michel Decima
James Kanze a écrit :
On May 27, 6:24 pm, Michel Decima wrote:
erreur : no match for 'operator==' in 'x == y'
Ce n'est pas la conversion implicite qu'il ne trouve pas. C'est opérateur == tout court. Les seuls opérateurs == dont il peut être question, ce sont ceux dans <string>. Sauf qu'ils ne sont pas des fonctions, mais des templates, et ne peuvent être pris en compte qu'une fois instantiées. Et la déduction automatique des types, qui permettrait l'instantiation, ne prend pas en compte les conversions implicites (ou seulement quelques conversions très simples, du genre tableau en pointeur).
Ok, merci a toi et a Fabien pour l'explication detaillee.
Par contre, si la classe encapsulee est template, alors ca ne compile plus :
template < typename T > struct Generic {} ; template < typename T > bool operator==( Generic< T > const&, Generic< T > const& ) { return true ; }
Wrapper< Generic< int > > x ; if ( x == Generic< int>() ) ; // KO : no match for 'operator=='
Donc, il semblerait que dans une expression d'egalité, l'operateur de conversion automatique de Wrapper< T > n'est pas trouvé lorsque T est lui-meme un type template.
La différence, ce n'est pas T, c'est l'operator==. Si l'operator== est une fonction templatée, la déduction de type, ne prenant pas en compte les conversions implicites, échouera, et il n'y aura pas de fonction à ajouter à l'ensemble de surcharge.
J'ai ajouté la classe template Generic<> pour essayer de comprendre d'ou venait le probleme, parce que je savais que std::string etait elle meme template. Mais comme tu le fais remarquer, c'etait une fausse piste, le probleme n'est pas dans le type T, mais dans la fonction appelée (l'exemple de Fabien avec la fonction f<>() le montre bien).
La solution évidente, c'est de ne jamais faire operator== un template. Faute d'autres solutions :
template< typename T > class Generic { public: friend bool operator==( Generic const& lhs, Generic const& rhs ) { return true /* ou d'autre chose */ ; } } ;
(Du coup, l'operator== n'est pas un template. Mais il y en a autant qu'il faut.)
Oui, ca marche, mais c'est quand meme subtil: on utilise le friend non pas pour donner l'accès aux membres proteges/prives de Generic, mais pour rendre operator== non template... Bref, c'est pas evident a la premiere lecture.
Pour std::string, évidement, il n'y a rien que tu puisses faire.
Et c'est bien mon probleme...
(Une question intéressante : est-ce qu'une implémentation de std::basic_string a le droit de faire comme ci-dessus ?)
Je ne sais pas.
James Kanze a écrit :
On May 27, 6:24 pm, Michel Decima <michel.dec...@orange-ft.com> wrote:
erreur : no match for 'operator==' in 'x == y'
Ce n'est pas la conversion implicite qu'il ne trouve pas. C'est
opérateur == tout court. Les seuls opérateurs == dont il peut
être question, ce sont ceux dans <string>. Sauf qu'ils ne sont
pas des fonctions, mais des templates, et ne peuvent être pris
en compte qu'une fois instantiées. Et la déduction automatique
des types, qui permettrait l'instantiation, ne prend pas en
compte les conversions implicites (ou seulement quelques
conversions très simples, du genre tableau en pointeur).
Ok, merci a toi et a Fabien pour l'explication detaillee.
Par contre, si la classe encapsulee est template, alors
ca ne compile plus :
template < typename T >
struct Generic {} ;
template < typename T >
bool operator==( Generic< T > const&, Generic< T > const& )
{ return true ; }
Wrapper< Generic< int > > x ;
if ( x == Generic< int>() ) ; // KO : no match for 'operator=='
Donc, il semblerait que dans une expression d'egalité,
l'operateur de conversion automatique de Wrapper< T > n'est
pas trouvé lorsque T est lui-meme un type template.
La différence, ce n'est pas T, c'est l'operator==. Si
l'operator== est une fonction templatée, la déduction de type,
ne prenant pas en compte les conversions implicites, échouera,
et il n'y aura pas de fonction à ajouter à l'ensemble de
surcharge.
J'ai ajouté la classe template Generic<> pour essayer de
comprendre d'ou venait le probleme, parce que je savais
que std::string etait elle meme template. Mais comme tu
le fais remarquer, c'etait une fausse piste, le probleme
n'est pas dans le type T, mais dans la fonction appelée
(l'exemple de Fabien avec la fonction f<>() le montre bien).
La solution évidente, c'est de ne jamais faire operator== un
template. Faute d'autres solutions :
template< typename T >
class Generic
{
public:
friend bool operator==( Generic const& lhs, Generic const&
rhs )
{
return true /* ou d'autre chose */ ;
}
} ;
(Du coup, l'operator== n'est pas un template. Mais il y en a
autant qu'il faut.)
Oui, ca marche, mais c'est quand meme subtil: on utilise
le friend non pas pour donner l'accès aux membres proteges/prives
de Generic, mais pour rendre operator== non template...
Bref, c'est pas evident a la premiere lecture.
Pour std::string, évidement, il n'y a rien que tu puisses faire.
Et c'est bien mon probleme...
(Une question intéressante : est-ce qu'une implémentation de
std::basic_string a le droit de faire comme ci-dessus ?)
Ce n'est pas la conversion implicite qu'il ne trouve pas. C'est opérateur == tout court. Les seuls opérateurs == dont il peut être question, ce sont ceux dans <string>. Sauf qu'ils ne sont pas des fonctions, mais des templates, et ne peuvent être pris en compte qu'une fois instantiées. Et la déduction automatique des types, qui permettrait l'instantiation, ne prend pas en compte les conversions implicites (ou seulement quelques conversions très simples, du genre tableau en pointeur).
Ok, merci a toi et a Fabien pour l'explication detaillee.
Par contre, si la classe encapsulee est template, alors ca ne compile plus :
template < typename T > struct Generic {} ; template < typename T > bool operator==( Generic< T > const&, Generic< T > const& ) { return true ; }
Wrapper< Generic< int > > x ; if ( x == Generic< int>() ) ; // KO : no match for 'operator=='
Donc, il semblerait que dans une expression d'egalité, l'operateur de conversion automatique de Wrapper< T > n'est pas trouvé lorsque T est lui-meme un type template.
La différence, ce n'est pas T, c'est l'operator==. Si l'operator== est une fonction templatée, la déduction de type, ne prenant pas en compte les conversions implicites, échouera, et il n'y aura pas de fonction à ajouter à l'ensemble de surcharge.
J'ai ajouté la classe template Generic<> pour essayer de comprendre d'ou venait le probleme, parce que je savais que std::string etait elle meme template. Mais comme tu le fais remarquer, c'etait une fausse piste, le probleme n'est pas dans le type T, mais dans la fonction appelée (l'exemple de Fabien avec la fonction f<>() le montre bien).
La solution évidente, c'est de ne jamais faire operator== un template. Faute d'autres solutions :
template< typename T > class Generic { public: friend bool operator==( Generic const& lhs, Generic const& rhs ) { return true /* ou d'autre chose */ ; } } ;
(Du coup, l'operator== n'est pas un template. Mais il y en a autant qu'il faut.)
Oui, ca marche, mais c'est quand meme subtil: on utilise le friend non pas pour donner l'accès aux membres proteges/prives de Generic, mais pour rendre operator== non template... Bref, c'est pas evident a la premiere lecture.
Pour std::string, évidement, il n'y a rien que tu puisses faire.
Et c'est bien mon probleme...
(Une question intéressante : est-ce qu'une implémentation de std::basic_string a le droit de faire comme ci-dessus ?)
Je ne sais pas.
James Kanze
On May 28, 10:34 am, Michel Decima wrote:
James Kanze a écrit :
[...]
> La solution évidente, c'est de ne jamais faire operator== un > template. Faute d'autres solutions :
> (Du coup, l'operator== n'est pas un template. Mais il y en a > autant qu'il faut.)
Oui, ca marche, mais c'est quand meme subtil: on utilise le friend non pas pour donner l'accès aux membres proteges/prives de Generic, mais pour rendre operator== non template... Bref, c'est pas evident a la premiere lecture.
En effet. Mais c'est un idiome plus ou moins répandu (due à Barton et Nackman).
Dans la pratique, j'ai quelque classes templatées de base qui le font :
template< typename T > class Generic : public ComparisonOperators< Generic > { // ... bool isEqual( Generic const& other ) const ; } ;
ComparisonOperators génère un operator== et un operator!= à partir de isEqual (et des opérateurs <, <=, > et >= à partir de compare, si cette fonction existe) ; ArithmeticOperators fait de même pour tous les opérateurs arithmétique, à partir des <op>= ; etc. Et la documentation de ces templates explique ce qui se passe.
> Pour std::string, évidement, il n'y a rien que tu puisses > faire.
Et c'est bien mon probleme...
Si il n'y a que std::string, tu peux fournir l'instance de l'operator== toi-même. Le problème devient plus ardu si tu as besoin de tous les basic_string possibles, de tous les vector, etc.
> (Une question intéressante : est-ce qu'une implémentation de > std::basic_string a le droit de faire comme ci-dessus ?)
Je ne sais pas.
Moi non plus. Mais je crains que non. La norme décrit la fonction comme un template, déclarée en dehors de la classe (et donc visible dans le namespace sans ADL). Et c'est bien trop facile à imaginer des cas où un programme verrait une différence, même si je doute que de tels cas se présentent dans du code réel.
-- 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
On May 28, 10:34 am, Michel Decima <michel.dec...@orange-ft.com>
wrote:
James Kanze a écrit :
[...]
> La solution évidente, c'est de ne jamais faire operator== un
> template. Faute d'autres solutions :
> (Du coup, l'operator== n'est pas un template. Mais il y en a
> autant qu'il faut.)
Oui, ca marche, mais c'est quand meme subtil: on utilise le
friend non pas pour donner l'accès aux membres proteges/prives
de Generic, mais pour rendre operator== non template... Bref,
c'est pas evident a la premiere lecture.
En effet. Mais c'est un idiome plus ou moins répandu (due à
Barton et Nackman).
Dans la pratique, j'ai quelque classes templatées de base qui le
font :
template< typename T >
class Generic : public ComparisonOperators< Generic >
{
// ...
bool isEqual( Generic const& other ) const ;
} ;
ComparisonOperators génère un operator== et un operator!= à
partir de isEqual (et des opérateurs <, <=, > et >= à partir de
compare, si cette fonction existe) ; ArithmeticOperators fait de
même pour tous les opérateurs arithmétique, à partir des <op>= ;
etc. Et la documentation de ces templates explique ce qui se
passe.
> Pour std::string, évidement, il n'y a rien que tu puisses
> faire.
Et c'est bien mon probleme...
Si il n'y a que std::string, tu peux fournir l'instance de
l'operator== toi-même. Le problème devient plus ardu si tu as
besoin de tous les basic_string possibles, de tous les vector,
etc.
> (Une question intéressante : est-ce qu'une implémentation de
> std::basic_string a le droit de faire comme ci-dessus ?)
Je ne sais pas.
Moi non plus. Mais je crains que non. La norme décrit la
fonction comme un template, déclarée en dehors de la classe (et
donc visible dans le namespace sans ADL). Et c'est bien trop
facile à imaginer des cas où un programme verrait une
différence, même si je doute que de tels cas se présentent dans
du code réel.
--
James Kanze (GABI Software) email:james.kanze@gmail.com
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
> (Du coup, l'operator== n'est pas un template. Mais il y en a > autant qu'il faut.)
Oui, ca marche, mais c'est quand meme subtil: on utilise le friend non pas pour donner l'accès aux membres proteges/prives de Generic, mais pour rendre operator== non template... Bref, c'est pas evident a la premiere lecture.
En effet. Mais c'est un idiome plus ou moins répandu (due à Barton et Nackman).
Dans la pratique, j'ai quelque classes templatées de base qui le font :
template< typename T > class Generic : public ComparisonOperators< Generic > { // ... bool isEqual( Generic const& other ) const ; } ;
ComparisonOperators génère un operator== et un operator!= à partir de isEqual (et des opérateurs <, <=, > et >= à partir de compare, si cette fonction existe) ; ArithmeticOperators fait de même pour tous les opérateurs arithmétique, à partir des <op>= ; etc. Et la documentation de ces templates explique ce qui se passe.
> Pour std::string, évidement, il n'y a rien que tu puisses > faire.
Et c'est bien mon probleme...
Si il n'y a que std::string, tu peux fournir l'instance de l'operator== toi-même. Le problème devient plus ardu si tu as besoin de tous les basic_string possibles, de tous les vector, etc.
> (Une question intéressante : est-ce qu'une implémentation de > std::basic_string a le droit de faire comme ci-dessus ?)
Je ne sais pas.
Moi non plus. Mais je crains que non. La norme décrit la fonction comme un template, déclarée en dehors de la classe (et donc visible dans le namespace sans ADL). Et c'est bien trop facile à imaginer des cas où un programme verrait une différence, même si je doute que de tels cas se présentent dans du code réel.
-- 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