C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Erreur de compilation d'un itérateur de map avec GCC

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (French)
View previous topic :: View next topic  
Author Message
Nicolas
Guest





PostPosted: Wed Jul 05, 2006 7:53 pm    Post subject: Erreur de compilation d'un itérateur de map avec GCC Reply with quote



Bonjour,

En essayant d'aider un ami sur un petit code C++, j'ai eu une erreur de
compilation de son code avec G++ sous GNU/Linux (versions 3.4.6 et
4.1.1). Le code est compilé par ICC 9.0 sous GNU/Linux et Microsoft CL
14.00.50727.42 (fourni avec Visual Studio 2005). Dans les deux cas
réussis, l'exécution me donne le résultat attendu.
Le message renvoyé par G++ me retient de poster un rapport de bug tout
de suite, car je ne suis pas sûr de ne pas avoir fait d'erreur dans ce
programme :

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

#include <iostream>
#include <map>
#include <string>

template <class T>
class C
{
public:
void listAll() {
for(std::map<std::string, T>::iterator i = box.begin();
i != box.end(); i++){
std::cout << i->first << " ---> "
<< i->second << std::endl;
}
}
std::map<std::string, T> box;
};

int main(int argc, char *argv[])
{

C<int> c0;
c0.box["abc"] = 123;
c0.box["def"] = 456;
c0.listAll();

C<std::string> c1;
c1.box["xx"] = "ref'd by xx";
c1.box["yy"] = "ref'd by yy";
c1.listAll();

return 0;
}

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

Avec ICC et CL, le binaire généré produit la sortie suivante :
abc ---> 123
def ---> 456
xx ---> ref'd by xx
yy ---> ref'd by yy

L'erreur de compilation par G++ se situe au niveau de l'itérateur sur le
map<std::string, T> :

test.cc: In member function `void C<T>::listAll()':
test.cc:10: erreur: expected `;' avant « i »
test.cc: In member function `void C<T>::listAll() [with T = int]':
test.cc:23: instantiated from here
test.cc:10: erreur: nom dépendant « std::map<std::basic_string<char,
std::char_traits<char>, std::allocator<char>
Quote:
,T,std::less<std::basic_string<char, std::char_traits<char>,
std::allocator<char> > >,std::allocator<std::pair<const

std::basic_string<char, std::char_traits<char>, std::allocator<char> >,
T> > >::iterator » est analysé comme un non type, mais son instantiation
le rend comme un type
test.cc:10: note: utiliser « typename std::map<std::basic_string<char,
std::char_traits<char>, std::allocator<char>
Quote:
,T,std::less<std::basic_string<char, std::char_traits<char>,
std::allocator<char> > >,std::allocator<std::pair<const

std::basic_string<char, std::char_traits<char>, std::allocator<char> >,
T> > >::iterator » si un type est désiré

Le message "::iterator est analysé comme un non type" me rend perplexe,
car c'est bien comme un type que je veux l'utiliser.
Il me semble alors que le conseil d'utiliser typename... est destiné aux
codeurs de map, non ?

Pourriez-vous m'éclairer sur cette erreur ?

Merci,

Nicolas.
Back to top
loufoque
Guest





PostPosted: Wed Jul 05, 2006 8:05 pm    Post subject: Re: Erreur de compilation d'un itérateur de map avec GCC Reply with quote



Nicolas a écrit :

Quote:
Pourriez-vous m'éclairer sur cette erreur ?

Je suppose que cela fonctionne si tu remplaces "std::map<std::string,
T>::iterator i" par "typename std::map<std::string, T>::iterator i" ?
Back to top
Nicolas
Guest





PostPosted: Wed Jul 05, 2006 8:33 pm    Post subject: Re: Erreur de compilation d'un itérateur de map avec GCC Reply with quote



loufoque wrote:
Quote:
Nicolas a écrit :

Pourriez-vous m'éclairer sur cette erreur ?

Je suppose que cela fonctionne si tu remplaces "std::map<std::string,
T>::iterator i" par "typename std::map<std::string, T>::iterator i" ?


En effet, merci !

L'explication de typename dans mon Stroustrup comporte justement un
exemple quasiment identique, avec vector<>.
Cela dit, il est précisé que "le compilateur pourrait examiner la
déclaration de vector<> afin de déterminer si l'itérateur (iterator)
dans vector<T>::iterator est un type. Cela n'est pas possible lorsque le
qualificatif est un paramètre type." (afin de repousser le test jusqu'au
moment où il est possible de le déterminer).
Il est précisé : "Cela constituerait toutefois une extension de langage
non standard".

Qui a raison dans ce cas ? GCC qui ne va pas chercher plus loin et qui
me demande un typename, ou ICC et CL qui se débrouillent pour comprendre
ce que je veux dire ? D'après les explications du livre, je dirais GCC,
mais est-ce standardisé ?

Nicolas.
Back to top
Franck Branjonneau
Guest





PostPosted: Wed Jul 05, 2006 10:21 pm    Post subject: Re: Erreur de compilation d'un itérateur de map avec GCC Reply with quote

Nicolas <n.favrefelix (AT) spamspamspameggsbaconandspamfree (DOT) fr> écrivait:

Quote:
[ Absence de typename sur type dependant ]

L'explication de typename dans mon Stroustrup [...].
Il est précisé : "Cela constituerait toutefois une extension de langage
non standard".

Voilà ;-)

Quote:
Qui a raison dans ce cas ? GCC qui ne va pas chercher plus loin et qui
me demande un typename, ou ICC et CL qui se débrouillent pour comprendre
ce que je veux dire ? D'après les explications du livre, je dirais GCC,
mais est-ce standardisé ?

Oui, g++ a raison. Le typename implicite est déprécié, en accord avec
le standard, depuis gcc-3.4 : <http://gcc.gnu.org/gcc-3.4/changes.html>
« You must now use the typename and template keywords to disambiguate
dependent names, as required by the C++ standard. »

--
Franck Branjonneau
Back to top
kanze
Guest





PostPosted: Thu Jul 06, 2006 9:11 am    Post subject: Re: Erreur de compilation d'un itérateur de map avec GCC Reply with quote

Franck Branjonneau wrote:
Quote:
Nicolas <n.favrefelix (AT) spamspamspameggsbaconandspamfree (DOT) fr
écrivait:

[ Absence de typename sur type dependant ]

Qui a raison dans ce cas ? GCC qui ne va pas chercher plus
loin et qui me demande un typename, ou ICC et CL qui se
débrouillent pour comprendre ce que je veux dire ? D'après
les explications du livre, je dirais GCC, mais est-ce
standardisé ?

Oui, g++ a raison. Le typename implicite est déprécié,

Ne tombons pas dans le même abus du langage que Microsoft. Le
typename implicit n'a pas été déprécié par ISO. Pour la simple
raison qu'en ce qui concerne ISO, il n'a jamais existé.

Quote:
en accord avec le standard, depuis gcc-3.4 :
http://gcc.gnu.org/gcc-3.4/changes.html> « You must now use
the typename and template keywords to disambiguate dependent
names, as required by the C++ standard. »

Je crois en effet que la norme exige une diagnostique. Qui
pourrait n'être qu'un avertissement ; si le compilateur a
accepté le code sans le typename avant (ce qui est le cas de
tous les compilateurs, je crois), il serait logique qu'il y a un
certain nombre de versions où il continue de l'accepter avec un
avertissement (et une option pour qu'il le réfuse), puis des
versions où c'est une erreur, mais il y a une option pour ne le
traiter que comme avertissement, et seulement au bout de longues
années, on en supprime le supporte complétement.

Selon la façon que sont implémenté les templates, en revanche,
ce n'est pas toujours évident de faire comme ça. Il faudrait
prèsque maintenir deux parseurs en parallel, ce qui implique un
boulot monstreux.

--
James Kanze GABI Software
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
Back to top
Franck Branjonneau
Guest





PostPosted: Thu Jul 06, 2006 4:56 pm    Post subject: Re: Erreur de compilation d'un itérateur de map avec GCC Reply with quote

"kanze" <kanze@gabi-soft.fr> écrivait:

Quote:
Franck Branjonneau wrote:
Nicolas <n.favrefelix (AT) spamspamspameggsbaconandspamfree (DOT) fr
écrivait:

[ Absence de typename sur type dependant ]

Qui a raison dans ce cas ? GCC qui ne va pas chercher plus
loin et qui me demande un typename, ou ICC et CL qui se
débrouillent pour comprendre ce que je veux dire ? D'après
les explications du livre, je dirais GCC, mais est-ce
standardisé ?

Oui, g++ a raison. Le typename implicite est déprécié,

Ne tombons pas dans le même abus du langage que Microsoft. Le
typename implicit n'a pas été déprécié par ISO. Pour la simple
raison qu'en ce qui concerne ISO, il n'a jamais existé.

en accord avec le standard, depuis gcc-3.4 :
http://gcc.gnu.org/gcc-3.4/changes.html> « You must now use
the typename and template keywords to disambiguate dependent
names, as required by the C++ standard. »

C'est toi qui abuse. Je n'ai pas parlé d'ISO, seulement de g++.

Quote:
Je crois en effet que la norme exige une diagnostique.

Tu n'abuses pas que du langage Wink Qui parle de diagnostic ?

Je doute que la norme exige un diagnostic. Mon argument est purement
logique : si tel était le cas alors les compilateurs pourraient
implémenter le typename implicite.

Quote:
Qui pourrait n'être qu'un avertissement ; si le compilateur a
accepté le code sans le typename avant (ce qui est le cas de tous
les compilateurs, je crois), il serait logique qu'il y a un certain
nombre de versions où il continue de l'accepter avec un
avertissement (et une option pour qu'il le réfuse), puis des
versions où c'est une erreur, mais il y a une option pour ne le
traiter que comme avertissement, et seulement au bout de longues
années, on en supprime le supporte complétement.

g++ n'a-t-il pas été progressif (avec des durées qui s'expriment en
mois) ?

Quote:
Selon la façon que sont implémenté les templates, en revanche,
ce n'est pas toujours évident de faire comme ça. Il faudrait
prèsque maintenir deux parseurs en parallel, ce qui implique un
boulot monstreux.

<http://gcc.gnu.org/bugs.html>
Standard conformance
« Non-conforming legacy code that worked with older versions of GCC
may be rejected by more recent compilers. There is no command-line
switch to ensure compatibility in general, because trying to parse
standard-conforming and old-style code at the same time would render
the C++ frontend unmaintainable. However, some non-conforming
constructs are allowed when the command-line option -fpermissive is
used. »

--
Franck Branjonneau
Back to top
kanze
Guest





PostPosted: Thu Jul 06, 2006 9:58 pm    Post subject: Re: Erreur de compilation d'un itérateur de map avec GCC Reply with quote

Franck Branjonneau wrote:
Quote:
"kanze" <kanze@gabi-soft.fr> écrivait:

Franck Branjonneau wrote:
Nicolas <n.favrefelix (AT) spamspamspameggsbaconandspamfree (DOT) fr
écrivait:

[ Absence de typename sur type dependant ]

Qui a raison dans ce cas ? GCC qui ne va pas chercher
plus loin et qui me demande un typename, ou ICC et CL qui
se débrouillent pour comprendre ce que je veux dire ?
D'après les explications du livre, je dirais GCC, mais
est-ce standardisé ?

Oui, g++ a raison. Le typename implicite est déprécié,

Ne tombons pas dans le même abus du langage que Microsoft.
Le typename implicit n'a pas été déprécié par ISO. Pour la
simple raison qu'en ce qui concerne ISO, il n'a jamais
existé.

en accord avec le standard, depuis gcc-3.4 :
http://gcc.gnu.org/gcc-3.4/changes.html> « You must now use
the typename and template keywords to disambiguate dependent
names, as required by the C++ standard. »

C'est toi qui abuse. Je n'ai pas parlé d'ISO, seulement de
g++.

Et Microsoft, sans doute, ne parle que de Microsoft. Vue
l'histoire que les gens en font, c'est mieux d'éviter le mot
déprécié, au moins que la contexte rend claire qui a déprécié
quoi.

Quote:
Je crois en effet que la norme exige une diagnostique.

Tu n'abuses pas que du langage Wink Qui parle de diagnostic ?

La norme C++.

Quote:
Je doute que la norme exige un diagnostic.

Je n'en suis pas vraiment sûr moi-même. Selon §14.6/4, « If a
specialization of a template is instantiated for a set of
template-arguments such that the qualified-name prefixed by
typename does not denote a type, the specialization is
ill-formed. » Mais je n'arrive pas le contraire : que c'est
une erreur si la spécialisation trouve un nom de type quand il
n'y avait pas de typename. Peut-être §14.6/6 : « The keyword
typename shall always be specified when the member is referred
to using a qualified name, even if the qualifier is simply the
class template name. » ; les exemples qui suivent le laissent
penser, en tout cas.

Quote:
Mon argument est purement logique : si tel était le cas alors
les compilateurs pourraient implémenter le typename implicite.

On parle de la norme ; je ne suis pas sûr que la logique y a
grand chose à voir:-). En fait, je ne suis pas trop ta
logique ; le résultat pratique serait que certain code serait
légal avec un compilateur, et illégal avec un autre. C'est une
situation déjà assez courant, mais que je préfère éviter quand
c'est possible.

Quote:
Qui pourrait n'être qu'un avertissement ; si le compilateur
a accepté le code sans le typename avant (ce qui est le cas
de tous les compilateurs, je crois), il serait logique qu'il
y a un certain nombre de versions où il continue de
l'accepter avec un avertissement (et une option pour qu'il
le réfuse), puis des versions où c'est une erreur, mais il y
a une option pour ne le traiter que comme avertissement, et
seulement au bout de longues années, on en supprime le
supporte complétement.

g++ n'a-t-il pas été progressif (avec des durées qui
s'expriment en mois) ?

Il a été assez progressif, mais effectivement, avec les durées
bien trop courtes.

Quote:
Selon la façon que sont implémenté les templates, en
revanche, ce n'est pas toujours évident de faire comme ça.
Il faudrait prèsque maintenir deux parseurs en parallel, ce
qui implique un boulot monstreux.

http://gcc.gnu.org/bugs.html
Standard conformance
« Non-conforming legacy code that worked with older versions
of GCC may be rejected by more recent compilers. There is no
command-line switch to ensure compatibility in general,
because trying to parse standard-conforming and old-style code
at the same time would render the C++ frontend unmaintainable.
However, some non-conforming constructs are allowed when the
command-line option -fpermissive is used. »

C'est à peu près ce que j'imaginais. Surtout le « would render
le frontend unmaintainable ». Comme partout, il y a un idéal,
et puis, il y a ce qu'on arrive à faire avec les moyens dont on
dispose. Je présume que l'équipe de g++ s'est trouvé en face du
même problème qu'on rencontre tous de temps en temps : on n'a
pas les ressources pour faire tout ce qu'on veut dans le temps
qu'il faut. Ils ont donc du faire un choix. Que le choix ait été
nécessaire, c'est regrettable, mais j'ai assez de compréhension
de ce qu'il y a derrière pour ne pas le critiquer.

--
James Kanze GABI Software
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
Back to top
Franck Branjonneau
Guest





PostPosted: Thu Jul 06, 2006 11:44 pm    Post subject: Re: Erreur de compilation d'un itérateur de map avec GCC Reply with quote

"kanze" <kanze@gabi-soft.fr> écrivait:

Quote:
Franck Branjonneau wrote:
"kanze" <kanze@gabi-soft.fr> écrivait:

Franck Branjonneau wrote:
Nicolas <n.favrefelix (AT) spamspamspameggsbaconandspamfree (DOT) fr
écrivait:

[ Absence de typename sur type dependant ]

Qui a raison dans ce cas ? GCC qui ne va pas chercher
plus loin et qui me demande un typename, ou ICC et CL qui
se débrouillent pour comprendre ce que je veux dire ?
D'après les explications du livre, je dirais GCC, mais
est-ce standardisé ?

Oui, g++ a raison. Le typename implicite est déprécié,

Ne tombons pas dans le même abus du langage que Microsoft.
Le typename implicit n'a pas été déprécié par ISO. Pour la
simple raison qu'en ce qui concerne ISO, il n'a jamais
existé.

en accord avec le standard, depuis gcc-3.4 :
http://gcc.gnu.org/gcc-3.4/changes.html> « You must now use
the typename and template keywords to disambiguate dependent
names, as required by the C++ standard. »

C'est toi qui abuse. Je n'ai pas parlé d'ISO, seulement de
g++.

Et Microsoft, sans doute, ne parle que de Microsoft. Vue
l'histoire que les gens en font, c'est mieux d'éviter le mot
déprécié, au moins que la contexte rend claire qui a déprécié
quoi.

Je n'avais pas compris ton objection.

Quote:
Je crois en effet que la norme exige une diagnostique.

Tu n'abuses pas que du langage Wink Qui parle de diagnostic ?

La norme C++.

Je doute que la norme exige un diagnostic.

Je n'en suis pas vraiment sûr moi-même. Selon §14.6/4, « If a
specialization of a template is instantiated for a set of
template-arguments such that the qualified-name prefixed by
typename does not denote a type, the specialization is
ill-formed. » Mais je n'arrive pas le contraire : que c'est
une erreur si la spécialisation trouve un nom de type quand il
n'y avait pas de typename. Peut-être §14.6/6 : « The keyword
typename shall always be specified when the member is referred
to using a qualified name, even if the qualifier is simply the
class template name. » ; les exemples qui suivent le laissent
penser, en tout cas.

C'est peut-être 16.6/7 qu'il fallait lire : « Knowing which names are
type names allows the syntax of every template definition to be
checked. No diagnostic shall be issued for a template definition for
which a valid specialization can be generated. If no valid
specialization can be generated for a template definition, and that
template is not instantiated, the template definition is ill-formed,
no diagnostic required. »

Quote:
Mon argument est purement logique : si tel était le cas alors
les compilateurs pourraient implémenter le typename implicite.

On parle de la norme ; je ne suis pas sûr que la logique y a
grand chose à voir:-). En fait, je ne suis pas trop ta
logique ; le résultat pratique serait que certain code serait
légal avec un compilateur, et illégal avec un autre. C'est une
situation déjà assez courant, mais que je préfère éviter quand
c'est possible.

Ce n'est pas tant une question de légalité que de qu'est-ce qui est fait.
L'exemple (contraint je te l'accorde Smile auquel je pensais

template< typename _T >
struct S {

int y_;

void
foo() {

for(_T:Mad * y_; y_ != 0; --y_) {

std::cout << "Foo: " << y_ << "\n";
}
}

void
bar() {

for(typename _T:Mad * y_; y_ != 0; --y_) {

std::cout << "Bar: " << y_ << "\n";
}
}

};

struct T1 {

static int const x= 1;

};

struct T2 {

typedef int x;

};


int
main() {
S< T1 > s1;
s1.foo();

S< T2 > s2;
s2.bar();
}

Que fait un compilateur qui utilises le typename implicite ?

-- Il transforme inconditionnellement foo en bar ?
-- Il postpone (c'est français ?) jusqu'à l'instanciation la
vérification du template ? J'ai alors foo< T1 > OK ; mais foo< T2 >()
devient bar< T2 >() là où je devrais avoir une erreur.

--
Franck Branjonneau
Back to top
kanze
Guest





PostPosted: Fri Jul 07, 2006 9:11 am    Post subject: Re: Erreur de compilation d'un itérateur de map avec GCC Reply with quote

Franck Branjonneau wrote:
Quote:
"kanze" <kanze@gabi-soft.fr> écrivait:

Franck Branjonneau wrote:
"kanze" <kanze@gabi-soft.fr> écrivait:

Franck Branjonneau wrote:
Nicolas <n.favrefelix (AT) spamspamspameggsbaconandspamfree (DOT) fr
écrivait:

[ Absence de typename sur type dependant ]

[...]
Quote:
Je crois en effet que la norme exige une diagnostique.

Tu n'abuses pas que du langage Wink Qui parle de diagnostic ?

La norme C++.

Je doute que la norme exige un diagnostic.

Je n'en suis pas vraiment sûr moi-même. Selon §14.6/4, « If a
specialization of a template is instantiated for a set of
template-arguments such that the qualified-name prefixed by
typename does not denote a type, the specialization is
ill-formed. » Mais je n'arrive pas le contraire : que c'est
une erreur si la spécialisation trouve un nom de type quand il
n'y avait pas de typename. Peut-être §14.6/6 : « The keyword
typename shall always be specified when the member is referred
to using a qualified name, even if the qualifier is simply the
class template name. » ; les exemples qui suivent le laissent
penser, en tout cas.

C'est peut-être 16.6/7 qu'il fallait lire : « Knowing which names are
type names allows the syntax of every template definition to be
checked. No diagnostic shall be issued for a template definition for
which a valid specialization can be generated. If no valid
specialization can be generated for a template definition, and that
template is not instantiated, the template definition is ill-formed,
no diagnostic required. »

Ça, je crois que c'est clair. Tant que le template n'est pas
instantié, une diagnostique est permise, mais non exigée.
D'après le premier passage que j'ai cité, c'est aussi clair que
si on spécifie typename, et que lors de l'instantiation, le nom
n'est pas un type, une diagnostique est exigée. Je ne trouve
rien d'aussi clair pour l'inverse, qu'on ne spécifie pas
typename, mais que le nom désigne un type. Mais je crois que
l'intention est bien qu'une diagnostique soit émise. C'est bien
ce cas-là où on pourrait parler du « typename implicit ». Et
c'est bien de légalité ou non de « typename implicit » que je
crois qu'on parlait.

Il y a, évidemment, un deuxième cas de « typename
implicit » : le cas où la contexte exige un nom de type, où
l'instantiation serait illégal si le nom n'est pas celui d'un
type, mais qu'on n'a pas mis « typename ». Dans ce cas-là,
c'est clair que tant que le template n'est pas instantié, aucune
diagnostique n'est exigée. Mais un template n'a de sens que si
on l'instantie, et dès qu'on l'instantie, ou bien, le nom
désigne un type, et on se rétrouve dans le cas ci-dessus, ou
bien le nom ne désigne pas un type, et si la contexte exige le
nom d'un type, on aurait bien une erreur de syntaxe qui exige
une diagnostique.

Quote:
Mon argument est purement logique : si tel était le cas
alors les compilateurs pourraient implémenter le typename
implicite.

On parle de la norme ; je ne suis pas sûr que la logique y a
grand chose à voir:-). En fait, je ne suis pas trop ta
logique ; le résultat pratique serait que certain code
serait légal avec un compilateur, et illégal avec un autre.
C'est une situation déjà assez courant, mais que je préfère
éviter quand c'est possible.

Ce n'est pas tant une question de légalité que de qu'est-ce
qui est fait. L'exemple (contraint je te l'accorde Smile auquel
je pensais

template< typename _T
struct S {

int y_;

void
foo() {

for(_T:Mad * y_; y_ != 0; --y_) {

std::cout << "Foo: " << y_ << "\n";
}
}

void
bar() {

for(typename _T:Mad * y_; y_ != 0; --y_) {

std::cout << "Bar: " << y_ << "\n";
}
}

};

struct T1 {

static int const x= 1;

};

struct T2 {

typedef int x;

};

int
main() {
S< T1 > s1;
s1.foo();

S< T2 > s2;
s2.bar();
}

Que fait un compilateur qui utilises le typename implicite ?

-- Il transforme inconditionnellement foo en bar ?

Il ne peut pas. Des transformations inconditionnelles ne
pourraient se faire que dans des cas du genre :
typedef _T:Mad X ;
Ici, si x n'est pas le nom d'un type, c'est une erreur. G++
4.1.0 donne une erreur, bien que le message d'erreur ne soit pas
du tout clair ; c'est sans doute que le parseur actuel a
réelement besoin du typename pour s'y rétrouver (ce qui cadre
bien avec leur explication pourquoi il n'y a pas d'option pour
le supporter -- il faudrait pratiquement un deuxième parseur).

Quote:
-- Il postpone (c'est français ?) jusqu'à l'instanciation la
vérification du template ?

C'est la solution « classique ». Avant la norme, prèque tous
(sinon tous) les compilateurs géraient les templates comme un
espèce de macro. Ils ne faisaient pour ainsi dire aucune
vérification (sauf qu'ils puissent correctement tokeniser le
texte) lors de la définition du template ; à l'instantiation,
ils remplacaient plus ou moins les noms de paramètres formels
avec les paramètres réels, et parser le code pour la première
fois.

Sans la distinction entre des noms dépendants et les autres, et
l'ajoute du typename (et template, dans certains cas), ce
n'était guère possible de faire autrement. Pour pouvoir parser
correctement le C++, il faut bien savoir ce qui est le nom d'un
type (ou dans certains contextes, le nom d'un template), et ce
qui ne l'est pas.

Quote:
J'ai alors foo< T1 > OK ; mais foo< T2 >()
devient bar< T2 >() là où je devrais avoir une erreur.

C'est à peu près ça, je crois.

--
James Kanze GABI Software
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
Back to top
Franck Branjonneau
Guest





PostPosted: Fri Jul 07, 2006 5:23 pm    Post subject: Re: Erreur de compilation d'un itérateur de map avec GCC Reply with quote

"kanze" <kanze@gabi-soft.fr> écrivait:

Quote:
Franck Branjonneau wrote:
"kanze" <kanze@gabi-soft.fr> écrivait:

Franck Branjonneau wrote:
"kanze" <kanze@gabi-soft.fr> écrivait:

Franck Branjonneau wrote:
Nicolas <n.favrefelix (AT) spamspamspameggsbaconandspamfree (DOT) fr
écrivait:

[ Absence de typename sur type dependant ]

[...]
Je crois en effet que la norme exige une diagnostique.

Tu n'abuses pas que du langage Wink Qui parle de diagnostic ?

La norme C++.

Je doute que la norme exige un diagnostic.

Je n'en suis pas vraiment sûr moi-même. Selon §14.6/4, « If a
specialization of a template is instantiated for a set of
template-arguments such that the qualified-name prefixed by
typename does not denote a type, the specialization is
ill-formed. » Mais je n'arrive pas le contraire : que c'est
une erreur si la spécialisation trouve un nom de type quand il
n'y avait pas de typename. Peut-être §14.6/6 : « The keyword
typename shall always be specified when the member is referred
to using a qualified name, even if the qualifier is simply the
class template name. » ; les exemples qui suivent le laissent
penser, en tout cas.

C'est peut-être 16.6/7 qu'il fallait lire : « Knowing which names are
type names allows the syntax of every template definition to be
checked. No diagnostic shall be issued for a template definition for
which a valid specialization can be generated. If no valid
specialization can be generated for a template definition, and that
template is not instantiated, the template definition is ill-formed,
no diagnostic required. »

Ça, je crois que c'est clair. Tant que le template n'est pas
instantié, une diagnostique est permise, mais non exigée.
D'après le premier passage que j'ai cité, c'est aussi clair que
si on spécifie typename, et que lors de l'instantiation, le nom
n'est pas un type, une diagnostique est exigée. Je ne trouve
rien d'aussi clair pour l'inverse, qu'on ne spécifie pas
typename, mais que le nom désigne un type.

Quel nom ? Pas le paramètre formel, si il n'est pas préfixé avec
typename ce n'est pas un type. C'est donc le paramètre réel. Mais où
est l'erreur : dans le template où dans le code l'utilisant ? Il
n'existe pas de moyen de choisir ; le diagnostic est au mieux une
indication que typename doit préfixer les noms de type et ce, non à la
vérification du template, mais à son instanciation.

Quote:
Mais je crois que l'intention est bien qu'une diagnostique soit
émise.

C'est dans la note de 14.6/7 « [Note: if a template is instantiated,
errors will be diagnosed according to the other rules in this
Standard. Exactly when these errors are diagnosed is a quality of
implementation issue. ] »

Quote:
C'est bien ce cas-là où on pourrait parler du « typename implicit
». Et c'est bien de légalité ou non de « typename implicit » que je
crois qu'on parlait.

Non pas de la légalité. C'est illégal : 14.6/3 « A qualified-name that
refers to a type and that depends on a template-parameter (14.6.2)
shall be prefixed by the keyword typename to indicate that the
qualified-name denotes a type ». Mais est-ce utilisable ? J'essaie de
montrer que non.

Quote:
Il y a, évidemment, un deuxième cas de « typename implicit » : le
cas où la contexte exige un nom de type, où l'instantiation serait
illégal si le nom n'est pas celui d'un type, mais qu'on n'a pas mis
« typename ». Dans ce cas-là, c'est clair que tant que le template
n'est pas instantié, aucune diagnostique n'est exigée. Mais un
template n'a de sens que si on l'instantie, et dès qu'on
l'instantie, ou bien, le nom désigne un type, et on se rétrouve dans
le cas ci-dessus, ou bien le nom ne désigne pas un type, et si la
contexte exige le nom d'un type, on aurait bien une erreur de
syntaxe qui exige une diagnostique.

Tu n'y est pas. Un template doit avoir un sens hors de toute
instanciation ; même s'il ne prend tout son sens qu'à
l'instanciation. Sens qui peut dépendre de ses paramètres réels :

template< typename _T >
_T null() { return 0; }

à un sens.

struct S1 { S1(int); };
struct S2 { S2(int const *); };

null< S1 >();
null< S2 >();
null< int >();
null< int * >();

ont des sémantiques distinctes.

L'absence de typename implicite ne te permet pas ce genre de «
manipulation ». C'est mon argument logique Wink En première phase le
compilateur n'est pas toujours en mesure de savoir si le typename est
requis ; et à l'instanciation il ignore qui est fautif le template ou
son utilisateur.

Quote:
[ Contre-exemple ]

Que fait un compilateur qui utilises le typename implicite ?

-- Il transforme inconditionnellement foo en bar ?

Il ne peut pas. Des transformations inconditionnelles ne
pourraient se faire que dans des cas du genre :
typedef _T:Mad X ;
Ici, si x n'est pas le nom d'un type, c'est une erreur. G++
4.1.0 donne une erreur, bien que le message d'erreur ne soit pas
du tout clair ; c'est sans doute que le parseur actuel a
réelement besoin du typename pour s'y rétrouver (ce qui cadre
bien avec leur explication pourquoi il n'y a pas d'option pour
le supporter -- il faudrait pratiquement un deuxième parseur).

OK.

Quote:
-- Il postpone (c'est français ?) jusqu'à l'instanciation la
vérification du template ?

C'est la solution « classique ». Avant la norme, prèque tous
(sinon tous) les compilateurs géraient les templates comme un
espèce de macro. Ils ne faisaient pour ainsi dire aucune
vérification (sauf qu'ils puissent correctement tokeniser le
texte) lors de la définition du template ; à l'instantiation,
ils remplacaient plus ou moins les noms de paramètres formels
avec les paramètres réels, et parser le code pour la première
fois.

Oui et ce n'est pas conforme.

Quote:
Sans la distinction entre des noms dépendants et les autres, et
l'ajoute du typename (et template, dans certains cas), ce
n'était guère possible de faire autrement. Pour pouvoir parser
correctement le C++, il faut bien savoir ce qui est le nom d'un
type (ou dans certains contextes, le nom d'un template), et ce
qui ne l'est pas.

J'ai alors foo< T1 > OK ; mais foo< T2 >()
devient bar< T2 >() là où je devrais avoir une erreur.

C'est à peu près ça, je crois.

L'absence de conformité conduit à accepter du code illégal et à en
transformer la sémantique : j'ai invoqué foo< T2 >() et j'obtiens bar<
T2 >() !

--
Franck Branjonneau
Back to top
James Kanze
Guest





PostPosted: Sun Jul 09, 2006 6:05 pm    Post subject: Re: Erreur de compilation d'un itérateur de map avec GCC Reply with quote

Franck Branjonneau wrote:
Quote:
"kanze" <kanze@gabi-soft.fr> écrivait:

[...]
Quote:
C'est peut-être 16.6/7 qu'il fallait lire : « Knowing which names are
type names allows the syntax of every template definition to be
checked. No diagnostic shall be issued for a template definition for
which a valid specialization can be generated. If no valid
specialization can be generated for a template definition, and that
template is not instantiated, the template definition is ill-formed,
no diagnostic required. »
Ça, je crois que c'est clair. Tant que le template n'est pas
instantié, une diagnostique est permise, mais non exigée.
D'après le premier passage que j'ai cité, c'est aussi clair que
si on spécifie typename, et que lors de l'instantiation, le nom
n'est pas un type, une diagnostique est exigée. Je ne trouve
rien d'aussi clair pour l'inverse, qu'on ne spécifie pas
typename, mais que le nom désigne un type.

Quel nom ? Pas le paramètre formel, si il n'est pas préfixé
avec typename ce n'est pas un type.

À la fin, le paramètre formel n'est que ce qu'est le paramètre
réel. Ensuite, la norme impose des restrictions sur le paramètre
réel (du genre, si on a spécifie typename, le paramètre formel
doit être tel que le nom est le nom d'un type). Finalement, la
norme autorise (mais n'exige pas) qu'une implémentation traite
une définition de template pour laquelle aucune instantiation
légale est possible comme erreur, même si on n'a pas essayé de
l'instantier.

(Ça, c'est des formalités de la norme. Le but, évidemment, c'est
de permettre la detection de d'avantage d'erreurs avant
l'instantiation.)

Quote:
C'est donc le paramètre réel. Mais où est l'erreur : dans le
template où dans le code l'utilisant ?

Qu'importe. Le programme est légal, ou il ne l'est pas. On ne
peut pas dire que un token « x » dans le code est légal en
dehors du contexte.

Dans la pratique, j'aurais tendance à dire que c'est l'appelant.
De même que si j'ai une fonction qui prend un char*, et
quelqu'un lui passe un int*. Mais du point de vue du langage,
cette distinction n'existe pas. L'erreur, c'est qu'il y a
désaccord. Passer un char* à une fonction, c'est bien légal, et
déclarer une fonction qui prend un int* aussi.

Ce qui peut se produire, évidemment, c'est que j'utilise
typename pour dire que le nom dépendant doit être un type, et
que le nom d'un type est illégal dans ce contexte. Alors, le
compilateur a le droit de considérer mon code comme erroné, et
réfuser de le compiler, même s'il n'y a jamais de paramètre
réel. De même, je crois (bien que je ne trouve pas où c'est
écrit), si je ne déclare pas de typename pour un nom dépendant,
1) c'est une erreur si un paramètre réel fait que le nom est le
nom d'un type, et 2) le compilateur peut (mais n'est pas obliger
de) le traiter comme une erreur si toute instantiation où le nom
ne désigne pas un type serait erronée.

C'est plus qu'un peu subtile. Je crois que le but, c'est de
permettre la détection d'un maximum d'erreurs lors de la
définition du template, tout en ne pas exigeant que le
compilateur le fasse. Ce qui est un concepte extrèmement
difficile à exprimer dans le langage de la norme.

Quote:
Il n'existe pas de moyen de choisir ; le diagnostic est au
mieux une indication que typename doit préfixer les noms de
type et ce, non à la vérification du template, mais à son
instanciation.

Formellement, le typename (ou son absense) exprime un contraint
sur les paramètres d'instantiation. Contraint qui, dans certains
contextes, existe de toute façon, considère :

template< class T > { typedef T:Mad x ; } ;

Même dans les templates d'avant la norme, il est clair qu'ici,
T:Mad doit nommer un type. Seulement, avant la norme, le
compilateur n'avait pas le droit de râler avant qu'il y avait
une instantiation. Et que dans le cas général, c'était assez
difficile, sinon impossible, pour que le compilateur sache
trouver même des erreurs les plus évidentes. Avec la règle que
l'auteur doit préciser (introduire une contrainte) si le nom est
nom d'un type ou non, le compilateur a beaucoup plus de
possibilité d'agir avant l'instantiation.

Quote:
Mais je crois que l'intention est bien qu'une diagnostique
soit émise.

C'est dans la note de 14.6/7 « [Note: if a template is
instantiated, errors will be diagnosed according to the other
rules in this Standard. Exactly when these errors are
diagnosed is a quality of implementation issue. ] »

C'est bien ce cas-là où on pourrait parler du « typename
implicit ». Et c'est bien de légalité ou non de « typename
implicit » que je crois qu'on parlait.

Non pas de la légalité. C'est illégal : 14.6/3 « A
qualified-name that refers to a type and that depends on a
template-parameter (14.6.2) shall be prefixed by the keyword
typename to indicate that the qualified-name denotes a type ».
Mais est-ce utilisable ? J'essaie de montrer que non.

Il y a, évidemment, un deuxième cas de « typename
implicit » : le cas où la contexte exige un nom de type, où
l'instantiation serait illégal si le nom n'est pas celui d'un
type, mais qu'on n'a pas mis « typename ». Dans ce cas-là,
c'est clair que tant que le template n'est pas instantié,
aucune diagnostique n'est exigée. Mais un template n'a de
sens que si on l'instantie, et dès qu'on l'instantie, ou
bien, le nom désigne un type, et on se rétrouve dans le cas
ci-dessus, ou bien le nom ne désigne pas un type, et si la
contexte exige le nom d'un type, on aurait bien une erreur de
syntaxe qui exige une diagnostique.

Tu n'y est pas. Un template doit avoir un sens hors de toute
instanciation ; même s'il ne prend tout son sens qu'à
l'instanciation.

Je ne crois pas qu'un template ait un sens hors de son
instanciation (mais évidemment, ça tourne sur la question que
signifie avoir un sens -- une question plutôt philosophique).

Quote:
Sens qui peut dépendre de ses paramètres réels :

template< typename _T
_T null() { return 0; }

à un sens.

struct S1 { S1(int); };
struct S2 { S2(int const *); };

null< S1 >();
null< S2 >();
null< int >();
null< int * >();

ont des sémantiques distinctes.

Et :
struct S3 {} ;
null< S3 >() ;
est illégal. Quel était le sens du template, alors ?

Quote:
L'absence de typename implicite ne te permet pas ce genre de
« manipulation ».

Tu m'as perdu quelque part ? Quel rapport ici avec des typename
implicits ? _T est le nom d'un type. Ce n'est pas implicit ;
on l'a déclaré comme tel dans la définition du template.

Quote:
C'est mon argument logique Wink En première phase le
compilateur n'est pas toujours en mesure de savoir si le
typename est requis ;

Prenons ton cas initial :

template< typename T >
void f()
{
T:Mad * p ;
}

Ici, à l'époque d'avant typename en tout cas, il y a une
contrainte que T soit de type classe et qu'il contient un membre
x. Si j'ai bien interprété la norme, ou l'intention de la norme,
aujourd'hui, l'absence du typename implique une contrainte
supplémentaire -- que x ne soit pas le nom d'un type.
Instantier alors f avec un T tel qu'il n'y a pas de T:Mad, OU que
T:Mad nomme un type, il y a une violation de contrainte. C'est
donc une erreur. A priori de l'appelant, mais au fond, c'est une
question plutôt du cahier de charges.

Note que si j'avais écrit plutôt :
typedef T:Mad x ;
il y avait bien une contrainte implicite que T:Mad nomme un type.
Et aussi une contrainte que T:Mad ne nomme pas un type (à cause
de l'absence du typename). Étant donné que c'est impossible que
T:Mad nomme un type et qu'il ne nomme pas de type, aucune
instantiation n'est possible, et le compilateur a le droit (mais
non l'obligation) de traiter le programme comme erroné, même
sans instantiation. (Nouveau droit, d'ailleurs, que la norme a
ajouté.)

[...]
Quote:
-- Il postpone (c'est français ?) jusqu'à l'instanciation la
vérification du template ?
C'est la solution « classique ». Avant la norme, prèque tous
(sinon tous) les compilateurs géraient les templates comme un
espèce de macro. Ils ne faisaient pour ainsi dire aucune
vérification (sauf qu'ils puissent correctement tokeniser le
texte) lors de la définition du template ; à l'instantiation,
ils remplacaient plus ou moins les noms de paramètres formels
avec les paramètres réels, et parser le code pour la première
fois.

Oui et ce n'est pas conforme.

Oui et non. Je crois que l'intention, c'était bien qu'il soit
conforme. À condition, évidemment, que le compilateur respecte
les règles de recherche de noms, et l'expression des contraintes
supplémentaires suite à l'introduction du typename. Sinon, même
aujourd'hui, il n'y aucune obligation du compilateur à signaler
une erreur avant l'instantiation.

Quote:
Sans la distinction entre des noms dépendants et les autres, et
l'ajoute du typename (et template, dans certains cas), ce
n'était guère possible de faire autrement. Pour pouvoir parser
correctement le C++, il faut bien savoir ce qui est le nom d'un
type (ou dans certains contextes, le nom d'un template), et ce
qui ne l'est pas.

J'ai alors foo< T1 > OK ; mais foo< T2 >()
devient bar< T2 >() là où je devrais avoir une erreur.
C'est à peu près ça, je crois.

L'absence de conformité conduit à accepter du code illégal et
à en transformer la sémantique : j'ai invoqué foo< T2 >() et
j'obtiens bar< T2 >() !

Non. Tu as invoqué foo<T2>, et tu obtiens un foo<T2> qui a une
sémantique proche de celui de bar<T2>. Mais c'est toujours
foo<T2>.

Dans ce cas précis, je crois que la norme exige une
diagnostique. Ensuite, c'est un comportement indéfini
(formellement) ; le compilateur peut faire ce qu'il veut.

Pour l'utilisateur, le cas ne diffère pas réelement de celui du
changement de la portée d'une variable déclarée dans un for.
Considérons, par exemple :

int
f()
{
for ( int i = 0 ; i < 10 ; ++ i ) {
}
return i ;
}

Dans ce cas-ci, g++ avait implémenté une solution idéale
d'évolution : le compilateur générait un message d'erreur (et
il y avait une option pour le supprimer), et continuait en se
servant de l'ancienne règle. Une telle solution serait aussi
idéale en ce qui concerne un typename qui manque.
Malheureusement, le travaille nécessaire à l'implémenter serait
beaucoup plus élevé.

--
James Kanze kanze.james (AT) neuf (DOT) fr
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
Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (French) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.