Erreur de compilation d'un itérateur de map avec GCC
Le
Nicolas
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>
>,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>
>,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.
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>
>,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>
>,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.

Poser une question


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 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.
Voilà ;-)
Oui, g++ a raison. Le typename implicite est déprécié, en accord avec
le standard, depuis gcc-3.4 : « You must now use the typename and template keywords to disambiguate
dependent names, as required by the C++ standard. »
--
Franck Branjonneau
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é.
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
C'est toi qui abuse. Je n'ai pas parlé d'ISO, seulement de g++.
Tu n'abuses pas que du langage ;-) 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.
g++ n'a-t-il pas été progressif (avec des durées qui s'expriment en
mois) ?
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