OVH Cloud OVH Cloud

enums anonymes

16 réponses
Avatar
Dimitri PAPADOPOULOS-ORFANOS
Bonjour,

Quelqu'un aurait-il l'amabilité de m'expliquer pourquoi le code suivant
serait invalide ? Messages d'erreur incompréhensibles gracieusement
fournis par gcc 4.0.0...

$ cat foo.cc
class MyClass {
};

template <typename T>
void
operator<<(MyClass& b, const T& t) {
}

enum { HSize };

int main() {
int i;
i<<HSize;
}
$
$ g++ foo.cc
foo.cc: In function 'int main()':
foo.cc:13: error: '<anonymous enum>' is/uses anonymous type
foo.cc:13: error: trying to instantiate 'template<class T> void
operator<<(MyClass&, const T&)'
$

--
Dimitri Papadopoulos

10 réponses

1 2
Avatar
kanze
Dimitri PAPADOPOULOS-ORFANOS wrote:

Quelqu'un aurait-il l'amabilité de m'expliquer pourquoi le
code suivant serait invalide ? Messages d'erreur
incompréhensibles gracieusement fournis par gcc 4.0.0...

$ cat foo.cc
class MyClass {
};

template <typename T>
void
operator<<(MyClass& b, const T& t) {
}

enum { HSize };

int main() {
int i;
i<<HSize;
}
$
$ g++ foo.cc
foo.cc: In function 'int main()':
foo.cc:13: error: '<anonymous enum>' is/uses anonymous type
foo.cc:13: error: trying to instantiate 'template<class T> void
operator<<(MyClass&, const T&)'
$


Le message ne me semble pas si mauvais que ça. Il dit exactement
ce que tu fais de faux : tu utilise un type anonyme en essayant
d'initialiser un template. Alors que c'est dit très
explicitement dans la norme que des types sans nom ne peuvent
pas servir ici. (J'avoue que pour une fois, la norme ne m'a pas
surpris en ce qui concerne les templates. Dans la mésure où la
norme exige en général que le type d'instantiation soit
globalement visible, je vois mal comment il permettrait des
instantiations sur des types anonyme.)

--
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
AG
wrote:
surpris en ce qui concerne les templates. Dans la mésure où la
Bonjour Monsieur,


Je me permet une petite remarque :

"Dans la mesure" au lieu de "Dans la mésure".

Comme personne n'ose/ne juge nécessaire de vous reprendre, et que vous
la faite souvent (ça n'a pas l'air d'être un oubli), et que vu votre
niveau de français, vous avez largement la capacité de l'intégrer, je me
suis permis cette petite remarque, en toute humilité.

Cordialement,

AG.

Avatar
kanze
AG wrote:
wrote:
surpris en ce qui concerne les templates. Dans la mésure où la


Je me permet une petite remarque :

"Dans la mesure" au lieu de "Dans la mésure".

Comme personne n'ose/ne juge nécessaire de vous reprendre, et
que vous la faite souvent (ça n'a pas l'air d'être un oubli),
et que vu votre niveau de français, vous avez largement la
capacité de l'intégrer, je me suis permis cette petite
remarque, en toute humilité.


Et je vous en remercie -- ce n'est qu'à travers de telles
remarques qu'on progresse, et c'est une fausse politesse de ne
pas les faire. (Normalement, on les fait par email, mais puisque
je n'affiche pas d'adresse email valable, mieux vaut ici que pas
du tout.)

En passant, je remarque qu'il y a effectivement deux choses que
ma tête refuse à retenir : les accents et les genres. Tous les
deux absents, évidemment, de ma langue d'origine. (Heureusement
qu'il n'y a ni l'un ni l'autre en C++.)

--
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
Dimitri PAPADOPOULOS-ORFANOS
Bonjour,

Le message ne me semble pas si mauvais que ça. Il dit exactement
ce que tu fais de faux : tu utilise un type anonyme en essayant
d'initialiser un template. Alors que c'est dit très


En fait ce que le code en question essaie de faire, c'est d'appliquer
l'opérateur de décalage << à un entier "i".

Or le compilateur n'accepte pas d'enum à droite de l'opérateur << de
décalage, ce qu'il acceptait jusqu'à présent :
i<<HSize;
Il veut absolument un int :
i<<int(HSize);

Du coup le compilateur va chercher un opérateur << ailleurs. Pour moi la
raison première pour laquelle cet opérateur ne peut s'appliquer n'est
pas une sombre histoire d'enum anonyme. C'est tout simplement que le
premier argument devrait être de type "MyClass" or l'argument "i" est un
int. Manifestement le compilateur bute sur le deuxième argument "HSize"
avant de buter sur le premier argument "i" ce qui serait naturel pour un
humain.

Le problème du message d'erreur c'est qu'il faut comprendre le
cheminement du compilateur avant de comprendre le message. Il y a deux
problèmes ici :

1) L'opérateur de décalage << n'accepte pas d'enum à la place d'un int
comme deuxième argument. Je suis d'accord qu'il n'y a pas encore erreur
à ce niveau, done le compilateur ne génère pas de message d'erreur pour
le moment.

2) Le compilateur cherche alors un opérateur << qui s'applique dans ce
cas. Le seul template qu'il trouve ne s'applique pas dans ce cas, de
façon évident si on regarde le premier argument et pour des raisons
obscures en ce qui concerne le deuxième argument. Or le compilateur
génère un message d'erreur pour le deuxième arguent, ce qui est
totalement contre-intuitif.

3) Le compilateur pourrait dans ce cas précis, après avoir constaté que
manifestement aucun template ne s'applique dans ce cas, revenir au point
1) et dire qu'un enum ne peut être utilisé à la place d'un int.


Au final, le message d'erreur devrait être :

foo.cc:13: error: '<anonymous enum>' cannot be cast to 'int'

au lieu de :

foo.cc:13: error: '<anonymous enum>' is/uses anonymous type
foo.cc:13: error: trying to instantiate 'template<class T> void
operator<<(MyClass&, const T&)'


explicitement dans la norme que des types sans nom ne peuvent
pas servir ici. (J'avoue que pour une fois, la norme ne m'a pas
surpris en ce qui concerne les templates. Dans la mésure où la
norme exige en général que le type d'instantiation soit
globalement visible, je vois mal comment il permettrait des
instantiations sur des types anonyme.)


Une fois de plus du code qui marchait pendant des années se met tout
d'un coup à ne plus fonctionner... Au moins cette fois-ci c'est moins
grave que d'habitude. Pas étonnant qu'on soit en train d'abandonner C++
pour Python dans mon labo (avec du C++ pour les parties à optimiser
quand même) : les thésards et post-docs même pointus en informatique
perdent leur temps à se battre avec les détails de la norme plutôt que
d'avancer dans leur travail.

--
Dimitri Papadopoulos

Avatar
Gabriel Dos Reis
Dimitri PAPADOPOULOS-ORFANOS writes:

| Bonjour,
|
| > Le message ne me semble pas si mauvais que ça. Il dit exactement
| > ce que tu fais de faux : tu utilise un type anonyme en essayant
| > d'initialiser un template. Alors que c'est dit très
|
| En fait ce que le code en question essaie de faire, c'est d'appliquer
| l'opérateur de décalage << à un entier "i".

Oui.

| Or le compilateur n'accepte pas d'enum à droite de l'opérateur << de
| décalage, ce qu'il acceptait jusqu'à présent :
| i<<HSize;
| Il veut absolument un int :
| i<<int(HSize);
|
| Du coup le compilateur va chercher un opérateur << ailleurs. Pour moi
| la raison première pour laquelle cet opérateur ne peut s'appliquer
| n'est pas une sombre histoire d'enum anonyme. C'est tout simplement
| que le premier argument devrait être de type "MyClass" or l'argument
| "i" est un int. Manifestement le compilateur bute sur le deuxième
| argument "HSize" avant de buter sur le premier argument "i" ce qui
| serait naturel pour un humain.

C'est exact.
Le fait que ce soit un code invalide est discutable. En fait, il y a
même un Core Issue pour ça.

Mon avis personnel que toute règle qui dit que ce code est invalde,
est simplment bugguée et doit être fixée. Cela n'a pas de sens.

-- Gaby
Avatar
Gabriel Dos Reis
writes:

[...]

| Alors que c'est dit très
| explicitement dans la norme que des types sans nom ne peuvent
| pas servir ici.

Ah bon. Alors même qu'il y a un Core Issue en discussion pour ça.

| (J'avoue que pour une fois, la norme ne m'a pas
| surpris en ce qui concerne les templates. Dans la mésure où la
| norme exige en général que le type d'instantiation soit
| globalement visible, je vois mal comment il permettrait des
| instantiations sur des types anonyme.)

Il n'y a aucun rapport.
(1) le type en question n'est pas utilisé pour faire des opérations
sur les templates;
(2) et même si l'énumération en question était utilisée pour faire
des opérations sur les templates, la notion de visibilité
concerne les *noms* et pas les types.

-- Gaby
Avatar
Gabriel Dos Reis
Dimitri PAPADOPOULOS-ORFANOS writes:

| Bonjour,
|
| Quelqu'un aurait-il l'amabilité de m'expliquer pourquoi le code
| suivant serait invalide ? Messages d'erreur incompréhensibles
| gracieusement fournis par gcc 4.0.0...

Un excès de zèle de la part du compilateur. Il y a un core issue pour
ça.

-- Gaby
Avatar
kanze
Gabriel Dos Reis wrote:
writes:

[...]

| Alors que c'est dit très
| explicitement dans la norme que des types sans nom ne peuvent
| pas servir ici.

Ah bon. Alors même qu'il y a un Core Issue en discussion pour ça.


C'est que je n'avais pas noté que l'opérand de gauche était un
int, et non la classe que voulait le template. J'ai lu trop
rapidement.

Alors, je suis étonné dans l'autre sens -- la conversion d'un
enum en int est bien défini, et dans la description des
opérateurs de décalage, il est même dit explicitement qu'on peut
se servir des enum. Alors, même si je ne suis pas très fort en
ce qui concerne les templates... il me semble que dans l'absense
d'une conversion int -> MyClass, le template ne vient même pas
en considération.

Quel est le core issue ? J'aurais cru que les promotions
intégrales fasse qu'il n'y a aucune différence ici entre l'enum
et un int.

| (J'avoue que pour une fois, la norme ne m'a pas surpris en
| ce qui concerne les templates. Dans la mésure où la norme
| exige en général que le type d'instantiation soit
| globalement visible, je vois mal comment il permettrait des
| instantiations sur des types anonyme.)

Il n'y a aucun rapport.


Je le reconnais. J'ai mal lu le code.

(1) le type en question n'est pas utilisé pour faire des
opérations

sur les templates;
(2) et même si l'énumération en question était utilisée pour
faire

des opérations sur les templates, la notion de visibilité
concerne les *noms* et pas les types.


En ce qui concerne (2), j'aurais cru que §14.3.1 (« A local
type, at type with no linkage, an UNNAMED TYPE or a type
compounded from any of these types shal not be used as a
template-argument for a template type-parameter. ») rendait le
code illégal. Mais comme j'ai dit, les règles en ce qui concerne
les templates sont trop compliquées pour que j'arrive réelement
à les comprendre.

Mon interprétation, ici, c'est que même si c'est le compilateur
qui a déduit le type, c'est le type qui est l'argument. Et que
le type en question n'a pas de nom. (Mais en pensant -- je
dirais aussi qu'un type comme int* n'a pas de nom. Or que je
suis assez sûr qu'on ne veut pas interdire ça comme paramètre
d'un template. Alors, je n'y comprends rien.)

--
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
Gabriel Dos Reis
writes:

[...]

| > (2) et même si l'énumération en question était utilisée pour
| faire
| > des opérations sur les templates, la notion de visibilité
| > concerne les *noms* et pas les types.
|
| En ce qui concerne (2), j'aurais cru que §14.3.1 (« A local
| type, at type with no linkage, an UNNAMED TYPE or a type
| compounded from any of these types shal not be used as a
| template-argument for a template type-parameter. ») rendait le
| code illégal. Mais comme j'ai dit, les règles en ce qui concerne
| les templates sont trop compliquées pour que j'arrive réelement
| à les comprendre.

Le passage que tu cites est vrai, mais au fond ce passage ne resiste
pas vraiment à l'analyse et je l'ai déjà dit dans EWG à Redmond
lorsque quelqu'un a proposé à Oxford (Avril 2003) d'autoriser les
types locaux en tant qu'arguments de template (tous les implémenteurs
à l'époque ont dit « oui », c'est faisable) et qu'à Redmond j'ai
entendu une « réticence » pour des raisons que j'ai oubliées.

J'ai entendu des rumeurs sur le mangling, mais comme je l'ai dejà fait
observé, ce n'est pas parce que quelque chose est local ou n'a pas
de nom qu'on ne peut ps le mangler. Pour le premier cas il suffit de
considérer les variables locales statiques et se rendre compte que
tous les compilateurs corrects les manglent (en ce qui concerne
l'idiome courant d'initialization). Pour le second cas, il suffit de
considérer les « unnamed namespace » et ce qu'ils contiennent.

| Mon interprétation, ici, c'est que même si c'est le compilateur
| qui a déduit le type, c'est le type qui est l'argument. Et que
| le type en question n'a pas de nom. (Mais en pensant -- je
| dirais aussi qu'un type comme int* n'a pas de nom. Or que je
| suis assez sûr qu'on ne veut pas interdire ça comme paramètre
| d'un template. Alors, je n'y comprends rien.)

Comme j'ai dit, quelque part quelqu'un a confondu « type » et
« nom » et a créé cette confusion. :-(
Au debut « linkage » concernait les noms, puis il a été étendu aux
types de fonctions (contrairement à l'avertissement de l'ARM) ; et
maintenant il est impliciement utilisé pour autres choses.

Le problème avec le code est que pour faire la résolution de surcharge
de « << », le compilateur doit considérer les « operator<< » dans la
portée ainsi que ceux qui sont câblés. En particulier, pour celui qui
est template, le compilateur doit déduire les arguments de
templates. Or cette phase (préliminaire) déduit un type non nommé,
alors le compilateur se plaint -- même et surtout si la fonction
correspondante n'est jamais utilisée. Intéressant, n'est-ce pas ?

On pourrait faire une règle disant que c'est invalide uniquement si la
fonction est choisie, mais je soupçonne que cela créerait toujours de
la confusion. Je pense que la solution est de simplement autoriser ces
choses là. La restriction est artificielle.

Ah, en ce qui concerne le core issue en question il n'y a pas encore
de numéro public mais c'est devant le CWG (i.e. reflecteur). Note
aussi l'explication

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#278

de Mike Miller tendrait à supporter la vision qu'on peut mangler une
énumération, même si elle n'a pas de nom :-)

-- Gaby
Avatar
kanze
Gabriel Dos Reis wrote:
writes:

[...]

| > (2) et même si l'énumération en question était utilisée
| > pour faire des opérations sur les templates, la
| > notion de visibilité concerne les *noms* et pas les
| > types.

| En ce qui concerne (2), j'aurais cru que §14.3.1 (« A local
| type, at type with no linkage, an UNNAMED TYPE or a type
| compounded from any of these types shal not be used as a
| template-argument for a template type-parameter. ») rendait le
| code illégal. Mais comme j'ai dit, les règles en ce qui concerne
| les templates sont trop compliquées pour que j'arrive réelement
| à les comprendre.

Le passage que tu cites est vrai, mais au fond ce passage ne
resiste pas vraiment à l'analyse et je l'ai déjà dit dans EWG
à Redmond lorsque quelqu'un a proposé à Oxford (Avril 2003)
d'autoriser les types locaux en tant qu'arguments de template
(tous les implémenteurs à l'époque ont dit « oui », c'est
faisable) et qu'à Redmond j'ai entendu une « réticence » pour
des raisons que j'ai oubliées.

J'ai entendu des rumeurs sur le mangling, mais comme je l'ai
dejà fait observé, ce n'est pas parce que quelque chose est
local ou n'a pas de nom qu'on ne peut ps le mangler. Pour le
premier cas il suffit de considérer les variables locales
statiques et se rendre compte que tous les compilateurs
corrects les manglent (en ce qui concerne l'idiome courant
d'initialization). Pour le second cas, il suffit de considérer
les « unnamed namespace » et ce qu'ils contiennent.

| Mon interprétation, ici, c'est que même si c'est le
| compilateur qui a déduit le type, c'est le type qui est
| l'argument. Et que le type en question n'a pas de nom. (Mais
| en pensant -- je dirais aussi qu'un type comme int* n'a pas
| de nom. Or que je suis assez sûr qu'on ne veut pas interdire
| ça comme paramètre d'un template. Alors, je n'y comprends
| rien.)

Comme j'ai dit, quelque part quelqu'un a confondu « type » et
« nom » et a créé cette confusion. :-( Au debut « linkage »
concernait les noms, puis il a été étendu aux types de
fonctions (contrairement à l'avertissement de l'ARM) ; et
maintenant il est impliciement utilisé pour autres choses.


En effet, je crois que tu as mis ton doigt sur le problème. Un
template dépend des types, non des « noms ». Seulement, comme tu
dis, ailleurs dans la norme, les types n'ont pas de linkage. Et
plus généralement, si on a déjà un type, que importe son nom.
(Ou ses noms -- grace aux typedef's, un type peut avoir
plusieurs noms aussi -- dont certains sans linkage, et d'autres
avec.)

Le problème avec le code est que pour faire la résolution de
surcharge de « << », le compilateur doit considérer les «
operator<< » dans la portée ainsi que ceux qui sont câblés. En
particulier, pour celui qui est template, le compilateur doit
déduire les arguments de templates. Or cette phase
(préliminaire) déduit un type non nommé, alors le compilateur
se plaint -- même et surtout si la fonction correspondante
n'est jamais utilisée. Intéressant, n'est-ce pas ?


Mais il me semble qu'il y avait quelque part une règle qui
disait que l'impossibilité de pouvoir deduire un template n'est
pas une erreur -- on écarte le template de l'ensemble de
surcharge, et on continue. Ou peut-être je confonds avec quelque
chose d'autre -- je sais que j'ai lu quelque chose du genre dans
Vandevoorde et Josuttis, mais c'est difficile à retenir tous ces
détails, quand on est obligé à travailler principalement avec un
compilateur qui ne les comprend pas, et qu'on ne s'en sert donc
jamais. (Ça doit dépendre des gens, mais je sais que moi, pour
retenir quelque chose, il faut que je m'en sers concrètement.)

On pourrait faire une règle disant que c'est invalide
uniquement si la fonction est choisie, mais je soupçonne que
cela créerait toujours de la confusion. Je pense que la
solution est de simplement autoriser ces choses là. La
restriction est artificielle.


En plus, tu ne peux jamais casser du code en enlevant une
restriction:-). (Comme tu sais, le code existant est une
préoccupation importante chez moi.)

Ah, en ce qui concerne le core issue en question il n'y a pas
encore de numéro public mais c'est devant le CWG (i.e.
reflecteur). Note aussi l'explication

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#278

de Mike Miller tendrait à supporter la vision qu'on peut
mangler une énumération, même si elle n'a pas de nom :-)


Je ne vois pas d'explication de Mike Miller là -- seulement des
commentaires de moi-même et de John Skaller.

On a une partie d'un problème parce que C se base sur l'idée des
types « compatibles », c-à-d plus ou moins l'identité
structurelles des types. C++ essaie d'aller dans la direction de
l'identité par nom. Mais j'ai parfois l'impression qu'on n'y a
pas formalisé assez. Il faudrait éventuellement définir un
concepte de linkage pour les types, par exemple. Ou au moins
définir exactement quand deux types n'en sont qu'un, et quand
ils ne le sont pas.

Aussi, je crois que essayer de définir l'identité des types par
la façon que fonctionne le mangling, c'est un peu d'inverser les
priorités -- il faudrait définir le mangling en fonction des
règles d'identité des types.

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

1 2