~logger()
{
cout << this << " " << this->str();
cout << endl ;
}
};
int main()
{
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
~logger()
{
cout << this << " " << this->str();
cout << endl ;
}
};
int main()
{
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
~logger()
{
cout << this << " " << this->str();
cout << endl ;
}
};
int main()
{
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
Résultat : Curieusement g++ me donne le résultat suivant
========= >
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
Résultat : Curieusement g++ me donne le résultat suivant
========= >
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
Résultat : Curieusement g++ me donne le résultat suivant
========= >
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
J'ai fait quelques essais et j'ai eu des
résultats étranges.
Je me demande donc si le code suivant est correct (il est très
court):
#include <sstream>
#include <string>
#include <iostream>
using namespace std;
class logger: public stringstream
{
public:
logger() {}
logger& operator()() { return(*this); }
~logger()
{
cout << this << " " << this->str();
cout << endl ;
}
};
int main()
{
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
logger log;
log << "jjj " << " kkk " << 1 ;
return(0);
}
Résultat : Curieusement g++ me donne le résultat suivant
==========
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
0x76ed40 jjj jjj 5
0x76ed40 jjj kkk 1
VC++6 :
006BFCD4 jjj jjj 5
006BFC48 jjj jjj 5
006BFD60 jjj kkk 1
En plus, ni g++ ni vc++6 ne me mettent de warnings.
Ai-je fait un "Undefined Behaviour" quelque part ?
( Je pense surtout au deux premiers cas, mais le destructeur
semble être appelé suffisamment tard car, sinon, il n'y aurait
aucun affichage ).
J'ai fait quelques essais et j'ai eu des
résultats étranges.
Je me demande donc si le code suivant est correct (il est très
court):
#include <sstream>
#include <string>
#include <iostream>
using namespace std;
class logger: public stringstream
{
public:
logger() {}
logger& operator()() { return(*this); }
~logger()
{
cout << this << " " << this->str();
cout << endl ;
}
};
int main()
{
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
logger log;
log << "jjj " << " kkk " << 1 ;
return(0);
}
Résultat : Curieusement g++ me donne le résultat suivant
==========
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
0x76ed40 jjj jjj 5
0x76ed40 jjj kkk 1
VC++6 :
006BFCD4 jjj jjj 5
006BFC48 jjj jjj 5
006BFD60 jjj kkk 1
En plus, ni g++ ni vc++6 ne me mettent de warnings.
Ai-je fait un "Undefined Behaviour" quelque part ?
( Je pense surtout au deux premiers cas, mais le destructeur
semble être appelé suffisamment tard car, sinon, il n'y aurait
aucun affichage ).
J'ai fait quelques essais et j'ai eu des
résultats étranges.
Je me demande donc si le code suivant est correct (il est très
court):
#include <sstream>
#include <string>
#include <iostream>
using namespace std;
class logger: public stringstream
{
public:
logger() {}
logger& operator()() { return(*this); }
~logger()
{
cout << this << " " << this->str();
cout << endl ;
}
};
int main()
{
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
logger log;
log << "jjj " << " kkk " << 1 ;
return(0);
}
Résultat : Curieusement g++ me donne le résultat suivant
==========
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
0x76ed40 jjj jjj 5
0x76ed40 jjj kkk 1
VC++6 :
006BFCD4 jjj jjj 5
006BFC48 jjj jjj 5
006BFD60 jjj kkk 1
En plus, ni g++ ni vc++6 ne me mettent de warnings.
Ai-je fait un "Undefined Behaviour" quelque part ?
( Je pense surtout au deux premiers cas, mais le destructeur
semble être appelé suffisamment tard car, sinon, il n'y aurait
aucun affichage ).
On Fri, 21 Oct 2005 01:24:20 +0200, plouf :~logger()
{
cout << this << " " << this->str();
cout << endl ;
Peux-tu garantir que ce code ne lance pas d'exception ?
}
};
int main()
{
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
Si je ne m'abuse, un temporaire est créé ; il existe pendant
toute l'exécution de l'expression (i.e. jusqu'au
point-virgule). Mais je n'en jurerais pas.
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
Idem.
J'ai testé sur Comeau ; ça donne :
Comeau C/C++ 4.3.3 (Jan 13 2004 11:29:09) for MS_WINDOWS_x86
Copyright 1988-2003 Comeau Computing. All rights reserved.
MODE:non-strict warnings borland C++
"logger.cpp", line 25: warning: initial value of reference to
non-const must
be an lvalue
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
^
Et à l'exécution :
1244848 jjj jjj 5
1244848 jjj jjj 5
1244600 jjj kkk 1
On Fri, 21 Oct 2005 01:24:20 +0200, plouf <plouf79@yahoo.fr>:
~logger()
{
cout << this << " " << this->str();
cout << endl ;
Peux-tu garantir que ce code ne lance pas d'exception ?
}
};
int main()
{
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
Si je ne m'abuse, un temporaire est créé ; il existe pendant
toute l'exécution de l'expression (i.e. jusqu'au
point-virgule). Mais je n'en jurerais pas.
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
Idem.
J'ai testé sur Comeau ; ça donne :
Comeau C/C++ 4.3.3 (Jan 13 2004 11:29:09) for MS_WINDOWS_x86
Copyright 1988-2003 Comeau Computing. All rights reserved.
MODE:non-strict warnings borland C++
"logger.cpp", line 25: warning: initial value of reference to
non-const must
be an lvalue
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
^
Et à l'exécution :
1244848 jjj jjj 5
1244848 jjj jjj 5
1244600 jjj kkk 1
On Fri, 21 Oct 2005 01:24:20 +0200, plouf :~logger()
{
cout << this << " " << this->str();
cout << endl ;
Peux-tu garantir que ce code ne lance pas d'exception ?
}
};
int main()
{
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
Si je ne m'abuse, un temporaire est créé ; il existe pendant
toute l'exécution de l'expression (i.e. jusqu'au
point-virgule). Mais je n'en jurerais pas.
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
Idem.
J'ai testé sur Comeau ; ça donne :
Comeau C/C++ 4.3.3 (Jan 13 2004 11:29:09) for MS_WINDOWS_x86
Copyright 1988-2003 Comeau Computing. All rights reserved.
MODE:non-strict warnings borland C++
"logger.cpp", line 25: warning: initial value of reference to
non-const must
be an lvalue
logger() << "jjj " << " jjj " << 5 ; // j'ai des doutes ici
^
Et à l'exécution :
1244848 jjj jjj 5
1244848 jjj jjj 5
1244600 jjj kkk 1
plouf writes:Résultat : Curieusement g++ me donne le résultat suivant
==========
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
C'est a mon avis un bug de g++ (sur lequel je suis tombe avant
hier; je n'ai pas encore pris le temps de m'assurer qu'il
etait connu et qu'il existait toujours dans les versions
encore supportees). Du moins tous les autres compilateurs que
j'ai essaye se comporte comme VC++.
plouf <plouf79@yahoo.fr> writes:
Résultat : Curieusement g++ me donne le résultat suivant
==========
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
C'est a mon avis un bug de g++ (sur lequel je suis tombe avant
hier; je n'ai pas encore pris le temps de m'assurer qu'il
etait connu et qu'il existait toujours dans les versions
encore supportees). Du moins tous les autres compilateurs que
j'ai essaye se comporte comme VC++.
plouf writes:Résultat : Curieusement g++ me donne le résultat suivant
==========
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
C'est a mon avis un bug de g++ (sur lequel je suis tombe avant
hier; je n'ai pas encore pris le temps de m'assurer qu'il
etait connu et qu'il existait toujours dans les versions
encore supportees). Du moins tous les autres compilateurs que
j'ai essaye se comporte comme VC++.
Jean-Marc Bourguet wrote:plouf writes:Résultat : Curieusement g++ me donne le résultat suivant
========= >
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
C'est a mon avis un bug de g++ (sur lequel je suis tombe avant
hier; je n'ai pas encore pris le temps de m'assurer qu'il
etait connu et qu'il existait toujours dans les versions
encore supportees). Du moins tous les autres compilateurs que
j'ai essaye se comporte comme VC++.
Tu n'as pas dû essayé Sun CC. Qui donne :
ffbee030 jjj kkk 1
ffbee0f8 jjj jjj 5
ffbee1c0 jjj jjj 5
(Régarde bien l'ordre des lignes, par rapport à VC++.)
Jean-Marc Bourguet wrote:
plouf <plouf79@yahoo.fr> writes:
Résultat : Curieusement g++ me donne le résultat suivant
========= >
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
C'est a mon avis un bug de g++ (sur lequel je suis tombe avant
hier; je n'ai pas encore pris le temps de m'assurer qu'il
etait connu et qu'il existait toujours dans les versions
encore supportees). Du moins tous les autres compilateurs que
j'ai essaye se comporte comme VC++.
Tu n'as pas dû essayé Sun CC. Qui donne :
ffbee030 jjj kkk 1
ffbee0f8 jjj jjj 5
ffbee1c0 jjj jjj 5
(Régarde bien l'ordre des lignes, par rapport à VC++.)
Jean-Marc Bourguet wrote:plouf writes:Résultat : Curieusement g++ me donne le résultat suivant
========= >
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
C'est a mon avis un bug de g++ (sur lequel je suis tombe avant
hier; je n'ai pas encore pris le temps de m'assurer qu'il
etait connu et qu'il existait toujours dans les versions
encore supportees). Du moins tous les autres compilateurs que
j'ai essaye se comporte comme VC++.
Tu n'as pas dû essayé Sun CC. Qui donne :
ffbee030 jjj kkk 1
ffbee0f8 jjj jjj 5
ffbee1c0 jjj jjj 5
(Régarde bien l'ordre des lignes, par rapport à VC++.)
Je m'attendrais à ce que Comeau donne la même chose (plus ou
moins) que g++ en mode strict. Mais il faut bien être en mode
strict.
Selon la norme, c'est g++ qui a raison. Mais il y a de raisons
historiques qui font que la plupart des compilateurs font autre
chose. (Ce qui m'étonne un peu, c'est que Sun CC n'a pas émis un
avertissement. Je m'attendais bien à un message au sujet d'un
"anachronism".)
Je m'attendrais à ce que Comeau donne la même chose (plus ou
moins) que g++ en mode strict. Mais il faut bien être en mode
strict.
Selon la norme, c'est g++ qui a raison. Mais il y a de raisons
historiques qui font que la plupart des compilateurs font autre
chose. (Ce qui m'étonne un peu, c'est que Sun CC n'a pas émis un
avertissement. Je m'attendais bien à un message au sujet d'un
"anachronism".)
Je m'attendrais à ce que Comeau donne la même chose (plus ou
moins) que g++ en mode strict. Mais il faut bien être en mode
strict.
Selon la norme, c'est g++ qui a raison. Mais il y a de raisons
historiques qui font que la plupart des compilateurs font autre
chose. (Ce qui m'étonne un peu, c'est que Sun CC n'a pas émis un
avertissement. Je m'attendais bien à un message au sujet d'un
"anachronism".)
"kanze" writes:Jean-Marc Bourguet wrote:plouf writes:Résultat : Curieusement g++ me donne le résultat suivant
========= > >
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
C'est a mon avis un bug de g++ (sur lequel je suis tombe avant
hier; je n'ai pas encore pris le temps de m'assurer qu'il
etait connu et qu'il existait toujours dans les versions
encore supportees). Du moins tous les autres compilateurs que
j'ai essaye se comporte comme VC++.
Tu n'as pas dû essayé Sun CC. Qui donne :
ffbee030 jjj kkk 1
ffbee0f8 jjj jjj 5
ffbee1c0 jjj jjj 5
Je l'ai fait sur mon exemple :-) Maitenant il va falloir que je
cherche la difference entre les deux.(Régarde bien l'ordre des lignes, par rapport à VC++.)
-features=tmplife
"kanze" <kanze@gabi-soft.fr> writes:
Jean-Marc Bourguet wrote:
plouf <plouf79@yahoo.fr> writes:
Résultat : Curieusement g++ me donne le résultat suivant
========= > >
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
C'est a mon avis un bug de g++ (sur lequel je suis tombe avant
hier; je n'ai pas encore pris le temps de m'assurer qu'il
etait connu et qu'il existait toujours dans les versions
encore supportees). Du moins tous les autres compilateurs que
j'ai essaye se comporte comme VC++.
Tu n'as pas dû essayé Sun CC. Qui donne :
ffbee030 jjj kkk 1
ffbee0f8 jjj jjj 5
ffbee1c0 jjj jjj 5
Je l'ai fait sur mon exemple :-) Maitenant il va falloir que je
cherche la difference entre les deux.
(Régarde bien l'ordre des lignes, par rapport à VC++.)
-features=tmplife
"kanze" writes:Jean-Marc Bourguet wrote:plouf writes:Résultat : Curieusement g++ me donne le résultat suivant
========= > >
G++/cygwin :
0x76ed40 0x436000 jjj 5 <== Voici ce que je ne comprends pas
C'est a mon avis un bug de g++ (sur lequel je suis tombe avant
hier; je n'ai pas encore pris le temps de m'assurer qu'il
etait connu et qu'il existait toujours dans les versions
encore supportees). Du moins tous les autres compilateurs que
j'ai essaye se comporte comme VC++.
Tu n'as pas dû essayé Sun CC. Qui donne :
ffbee030 jjj kkk 1
ffbee0f8 jjj jjj 5
ffbee1c0 jjj jjj 5
Je l'ai fait sur mon exemple :-) Maitenant il va falloir que je
cherche la difference entre les deux.(Régarde bien l'ordre des lignes, par rapport à VC++.)
-features=tmplife
"kanze" writes:
Je m'attendrais à ce que Comeau donne la même chose (plus ou
moins) que g++ en mode strict. Mais il faut bien être en mode
strict.
J'ai pas essaye comeau (c'est chez moi que j'en dispose, le
probleme je l'ai trouve au boulot)
Selon la norme, c'est g++ qui a raison. Mais il y a de raisons
historiques qui font que la plupart des compilateurs font
autre chose. (Ce qui m'étonne un peu, c'est que Sun CC n'a pas
émis un avertissement. Je m'attendais bien à un message au
sujet d'un "anachronism".)
Il donne un warning avec +w2 (malheureusement il y a trop de
faux positifs a ce niveau; je ne me serais pas trompe de
coupable si j'avais vu le warning).
Je n'arrive pas a le faire passer dans un mode plus standard,
meme avec
-features=%all,no%extensions,no%iddollar,no%transitions
il ne se comporte pas comme g++ (je m'attendais a ce que le
probleme de binding de reference non constante soit controle
avec no%transition).
"kanze" <kanze@gabi-soft.fr> writes:
Je m'attendrais à ce que Comeau donne la même chose (plus ou
moins) que g++ en mode strict. Mais il faut bien être en mode
strict.
J'ai pas essaye comeau (c'est chez moi que j'en dispose, le
probleme je l'ai trouve au boulot)
Selon la norme, c'est g++ qui a raison. Mais il y a de raisons
historiques qui font que la plupart des compilateurs font
autre chose. (Ce qui m'étonne un peu, c'est que Sun CC n'a pas
émis un avertissement. Je m'attendais bien à un message au
sujet d'un "anachronism".)
Il donne un warning avec +w2 (malheureusement il y a trop de
faux positifs a ce niveau; je ne me serais pas trompe de
coupable si j'avais vu le warning).
Je n'arrive pas a le faire passer dans un mode plus standard,
meme avec
-features=%all,no%extensions,no%iddollar,no%transitions
il ne se comporte pas comme g++ (je m'attendais a ce que le
probleme de binding de reference non constante soit controle
avec no%transition).
"kanze" writes:
Je m'attendrais à ce que Comeau donne la même chose (plus ou
moins) que g++ en mode strict. Mais il faut bien être en mode
strict.
J'ai pas essaye comeau (c'est chez moi que j'en dispose, le
probleme je l'ai trouve au boulot)
Selon la norme, c'est g++ qui a raison. Mais il y a de raisons
historiques qui font que la plupart des compilateurs font
autre chose. (Ce qui m'étonne un peu, c'est que Sun CC n'a pas
émis un avertissement. Je m'attendais bien à un message au
sujet d'un "anachronism".)
Il donne un warning avec +w2 (malheureusement il y a trop de
faux positifs a ce niveau; je ne me serais pas trompe de
coupable si j'avais vu le warning).
Je n'arrive pas a le faire passer dans un mode plus standard,
meme avec
-features=%all,no%extensions,no%iddollar,no%transitions
il ne se comporte pas comme g++ (je m'attendais a ce que le
probleme de binding de reference non constante soit controle
avec no%transition).
plouf wrote:J'ai fait quelques essais et j'ai eu des
résultats étranges.
Le code même me semble un peu étrange:-).
C'est légal, mais ça ne fait pas ce que tu pourrais penser.
L'expression logger() est un temporaire. Il ne peut donc pas
servir à l'initialisation d'une référence. Or, dans les
opérateur << qui sont des fonctions libres, le premier paramètre
est bien une référence vers un non-const. Ils sont donc exclus
de l'ensemble de surcharge. En revanche, on peut appeler une
fonction membre non-const, et les opérateurs << membre sont pris
en considération.
Selon la norme, << char const* n'est pas membre, mais fonction
globale. Il ne fait donc pas partie de l'ensemble considéré pour
la résolution du surcharge. En revanche, un char const* peut se
convertir implicitement en void const*, et l'opérateur << void
const* est, selon la norme, membre. Et étant donné que c'est ici
la seule fonction qu'on peut appelée, c'est ce que doit trouver
la résolution du surcharge. On va donc afficher l'adresse de la
chaîne.
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
Aucun problème : tu peux appeler une fonction membre, même non
const sur un objet temporaire. Et ton operator() renvoie un
lvalue non const ; il joue le rôle que jouait << "" autrefois.
Tout compte fait :
Les règles de C++ ici sont ce qu'elles sont. Il y a des cas où
elles prètent à confusion, mais elles sont motivées par des
considérations pratiques, et de l'expérience réele. Et moi,
personnellement, même si je n'aime pas la confusion, j'ai dû mal
à voir comment l'ôter sans créer d'autres problèmes plus graves.
En revanche, c'est peut-être un peu dommage que le problème se
présente d'une façon si bizarre dans la bibliothèque standard.
Une solution, peut-être, serait de faire que tous les opérateurs
<< soient des fonctions globales, ce qui fera au moins qu'on les
voit tous, ou aucun, et soit qu'il y a une erreur à la
compilation, soit que le compilateur choisit le surcharge auquel
on s'attend.
Je trouve aussi ton idée d'utiliser l'operator()() pour la
conversion en lvalue assez bonne.
plouf wrote:
J'ai fait quelques essais et j'ai eu des
résultats étranges.
Le code même me semble un peu étrange:-).
C'est légal, mais ça ne fait pas ce que tu pourrais penser.
L'expression logger() est un temporaire. Il ne peut donc pas
servir à l'initialisation d'une référence. Or, dans les
opérateur << qui sont des fonctions libres, le premier paramètre
est bien une référence vers un non-const. Ils sont donc exclus
de l'ensemble de surcharge. En revanche, on peut appeler une
fonction membre non-const, et les opérateurs << membre sont pris
en considération.
Selon la norme, << char const* n'est pas membre, mais fonction
globale. Il ne fait donc pas partie de l'ensemble considéré pour
la résolution du surcharge. En revanche, un char const* peut se
convertir implicitement en void const*, et l'opérateur << void
const* est, selon la norme, membre. Et étant donné que c'est ici
la seule fonction qu'on peut appelée, c'est ce que doit trouver
la résolution du surcharge. On va donc afficher l'adresse de la
chaîne.
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
Aucun problème : tu peux appeler une fonction membre, même non
const sur un objet temporaire. Et ton operator() renvoie un
lvalue non const ; il joue le rôle que jouait << "" autrefois.
Tout compte fait :
Les règles de C++ ici sont ce qu'elles sont. Il y a des cas où
elles prètent à confusion, mais elles sont motivées par des
considérations pratiques, et de l'expérience réele. Et moi,
personnellement, même si je n'aime pas la confusion, j'ai dû mal
à voir comment l'ôter sans créer d'autres problèmes plus graves.
En revanche, c'est peut-être un peu dommage que le problème se
présente d'une façon si bizarre dans la bibliothèque standard.
Une solution, peut-être, serait de faire que tous les opérateurs
<< soient des fonctions globales, ce qui fera au moins qu'on les
voit tous, ou aucun, et soit qu'il y a une erreur à la
compilation, soit que le compilateur choisit le surcharge auquel
on s'attend.
Je trouve aussi ton idée d'utiliser l'operator()() pour la
conversion en lvalue assez bonne.
plouf wrote:J'ai fait quelques essais et j'ai eu des
résultats étranges.
Le code même me semble un peu étrange:-).
C'est légal, mais ça ne fait pas ce que tu pourrais penser.
L'expression logger() est un temporaire. Il ne peut donc pas
servir à l'initialisation d'une référence. Or, dans les
opérateur << qui sont des fonctions libres, le premier paramètre
est bien une référence vers un non-const. Ils sont donc exclus
de l'ensemble de surcharge. En revanche, on peut appeler une
fonction membre non-const, et les opérateurs << membre sont pris
en considération.
Selon la norme, << char const* n'est pas membre, mais fonction
globale. Il ne fait donc pas partie de l'ensemble considéré pour
la résolution du surcharge. En revanche, un char const* peut se
convertir implicitement en void const*, et l'opérateur << void
const* est, selon la norme, membre. Et étant donné que c'est ici
la seule fonction qu'on peut appelée, c'est ce que doit trouver
la résolution du surcharge. On va donc afficher l'adresse de la
chaîne.
logger()() << "jjj " << " jjj " << 5 ; // et ici aussi
Aucun problème : tu peux appeler une fonction membre, même non
const sur un objet temporaire. Et ton operator() renvoie un
lvalue non const ; il joue le rôle que jouait << "" autrefois.
Tout compte fait :
Les règles de C++ ici sont ce qu'elles sont. Il y a des cas où
elles prètent à confusion, mais elles sont motivées par des
considérations pratiques, et de l'expérience réele. Et moi,
personnellement, même si je n'aime pas la confusion, j'ai dû mal
à voir comment l'ôter sans créer d'autres problèmes plus graves.
En revanche, c'est peut-être un peu dommage que le problème se
présente d'une façon si bizarre dans la bibliothèque standard.
Une solution, peut-être, serait de faire que tous les opérateurs
<< soient des fonctions globales, ce qui fera au moins qu'on les
voit tous, ou aucun, et soit qu'il y a une erreur à la
compilation, soit que le compilateur choisit le surcharge auquel
on s'attend.
Je trouve aussi ton idée d'utiliser l'operator()() pour la
conversion en lvalue assez bonne.