OVH Cloud OVH Cloud

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

9 réponses
Avatar
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.

9 réponses

Avatar
loufoque

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" ?

Avatar
Nicolas
loufoque wrote:

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.


Avatar
Franck Branjonneau
Nicolas écrivait:

[ 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à ;-)

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

Avatar
kanze
Franck Branjonneau wrote:
Nicolas
é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. »


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


Avatar
Franck Branjonneau
"kanze" écrivait:

Franck Branjonneau wrote:
Nicolas
é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++.

Je crois en effet que la norme exige une diagnostique.


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.

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) ?

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



Avatar
kanze
Franck Branjonneau wrote:
"kanze" écrivait:

Franck Branjonneau wrote:
Nicolas
é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 crois en effet que la norme exige une diagnostique.


Tu n'abuses pas que du langage ;-) 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.

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.

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.

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




Avatar
Franck Branjonneau
"kanze" écrivait:

Franck Branjonneau wrote:
"kanze" écrivait:

Franck Branjonneau wrote:
Nicolas
é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.

Je crois en effet que la norme exige une diagnostique.


Tu n'abuses pas que du langage ;-) 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. »

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 :-) auquel je pensais

template< typename _T >
struct S {

int y_;

void
foo() {

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

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

void
bar() {

for(typename _T::x * 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





Avatar
kanze
Franck Branjonneau wrote:
"kanze" écrivait:

Franck Branjonneau wrote:
"kanze" écrivait:

Franck Branjonneau wrote:
Nicolas
écrivait:

[ Absence de typename sur type dependant ]







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


Tu n'abuses pas que du langage ;-) 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.

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 :-) auquel
je pensais

template< typename _T >
struct S {

int y_;

void
foo() {

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

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

void
bar() {

for(typename _T::x * 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::x 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).

-- 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.

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






Avatar
Franck Branjonneau
"kanze" écrivait:

Franck Branjonneau wrote:
"kanze" écrivait:

Franck Branjonneau wrote:
"kanze" écrivait:

Franck Branjonneau wrote:
Nicolas
écrivait:

[ Absence de typename sur type dependant ]







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


Tu n'abuses pas que du langage ;-) 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.

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. 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 ;-) 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.

[ 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::x 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.

-- 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.

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