OVH Cloud OVH Cloud

Passage g++ 3.3 --> 3.4 : erreur typedef

5 réponses
Avatar
Vincent Richard
Bonjour,

Je viens de passer à la version 3.4 de g++ et le code suivant ne
compile plus :

class A { };

typedef A test; // ligne 8

class B
{
private:

class test* m_test; // ligne 15

public:

class test* test() { return m_test; } // ligne 19
};

L'erreur :

td.cpp:15: error: using typedef-name `test' after `class'
td.cpp:19: error: using typedef-name `test' after `class'

Si j'enlève les 'class' (l.15 et l.18), ça passe avec la version 3.4 mais ça
ne compile plus avec la version 3.3 :

td.cpp:19: error: declaration of `test* B::test()'
td.cpp:8: error: changes meaning of `test' from `typedef class A test'

Comment résoudre le problème en gardant le même nom pour la fonction, sans
renommer la classe et pour que ça fonctionne avec les deux version des
compilateurs ?

Merci d'avance pour vos réponses.

Vincent

--
vmime, une bibliothèque C++ sous licence GPL pour parser et générer
des messages au format MIME : http://www.sourceforge.net/projects/vmime/

5 réponses

Avatar
Horst Kraemer
On Sun, 25 Apr 2004 10:44:34 +0200, Vincent Richard
wrote:

Je viens de passer à la version 3.4 de g++ et le code suivant ne
compile plus :

class A { };

typedef A test; // ligne 8

class B
{
private:

class test* m_test; // ligne 15

public:

class test* test() { return m_test; } // ligne 19
};

L'erreur :

td.cpp:15: error: using typedef-name `test' after `class'
td.cpp:19: error: using typedef-name `test' after `class'


'class test' était toujours illégal selon la norme (7.1.3, par.4)
après

class A;
typedef A test;

bien qu'il y ait des compilateurs qui l'acceptent.


Si j'enlève les 'class' (l.15 et l.18), ça passe avec la version 3.4 mais ça
ne compile plus avec la version 3.3 :

td.cpp:19: error: declaration of `test* B::test()'
td.cpp:8: error: changes meaning of `test' from `typedef class A test'


C'est normal. 'test' est un nom de classe qui est au même niveau qu'un
nom de fonction. C'est aussi illégal que

class A;

void A();



Comment résoudre le problème en gardant le même nom pour la fonction, sans
renommer la classe et pour que ça fonctionne avec les deux version des
compilateurs ?


Impossihle. Un typedef 'test' et une fonction 'test' ne peuvent pas
coexister et 'class test' est illégal si test est un typedef.


Laisser tomber le typedef. Quel est l'intérêt ? Ou changer le nom du
typedef.

--
Horst

Avatar
Vincent Richard

class A { };

typedef A test; // ligne 8

class B
{
private:

class test* m_test; // ligne 15

public:

class test* test() { return m_test; } // ligne 19
};

[...]
Si j'enlève les 'class' (l.15 et l.18), ça passe avec la version 3.4 mais
ça ne compile plus avec la version 3.3 :

td.cpp:19: error: declaration of `test* B::test()'
td.cpp:8: error: changes meaning of `test' from `typedef class A test'


C'est normal. 'test' est un nom de classe qui est au même niveau qu'un
nom de fonction.


Pourtant l'un est un type (défini dans l'espace de nom global, puisque
"class test" est censé y faire référence et non déclarer un nouveau type)
et l'autre une fonction (définie dans la classe B). Et la version 3.4 de
g++ fait bien la différence puisque je peux écrire :

class test;

struct B { test* test(); }

qui est correct, me semble-t-il.

Mais je pense que c'est une limitation du parseur de g++ 3.3 qui ne
parvient pas à distinguer les deux cas (contexte).

Comment résoudre le problème en gardant le même nom pour la fonction,
sans renommer la classe et pour que ça fonctionne avec les deux version
des compilateurs ?


Impossihle. Un typedef 'test' et une fonction 'test' ne peuvent pas
coexister et 'class test' est illégal si test est un typedef.

Laisser tomber le typedef. Quel est l'intérêt ? Ou changer le nom du
typedef.


Ici, j'utilise le typedef pour faire un alias d'un nom de classe (pour la
clarté du code). Pour éviter des #define...

J'ai trouvé une solution provisoire, mais pas très élégante :

class A { };

class test : public A { };

class B
{
class test* test() { ... }
}

Vincent

--
vmime, une bibliothèque C++ sous licence GPL pour parser et générer
des messages au format MIME : http://www.sourceforge.net/projects/vmime/


Avatar
Vincent Richard

J'ai trouvé une solution provisoire, mais pas très élégante :

class A { };

class test : public A { };

class B
{
class test* test() { ... }
}


Je viens de me rendre compte que je pouvais écrire tout simplement :

namespace X
{
class A { };

typedef A test;

class B
{
X::test m_test;
X::test* test() { return m_test; }
}
};

Désolé pour le dérangement...

Vincent

--
vmime, une bibliothèque C++ sous licence GPL pour parser et générer
des messages au format MIME : http://www.sourceforge.net/projects/vmime/

Avatar
Gabriel Dos Reis
Vincent Richard writes:

| class test;
|
| struct B { test* test(); }
|
| qui est correct, me semble-t-il.


Non. C'est invalide -- mais la norme ne demande pas de diagnostique.
Les récentes versions de GCC avant 3.4.0 arrivaient à le diagnostique
mais quelqu'un introduit une regression dans le compilatur à ce
niveau. Je me rappelle que le problème a été rapporté il y a peu.

-- Gaby
Avatar
kanze
Vincent Richard wrote
in message news:<408bbd90$0$19503$...

class A { };

typedef A test; // ligne 8

class B
{
private:

class test* m_test; // ligne 15

public:

class test* test() { return m_test; } // ligne 19
};

[...]
Si j'enlève les 'class' (l.15 et l.18), ça passe avec la version
3.4 mais ça ne compile plus avec la version 3.3 :

td.cpp:19: error: declaration of `test* B::test()'
td.cpp:8: error: changes meaning of `test' from `typedef class A test'


C'est normal. 'test' est un nom de classe qui est au même niveau
qu'un nom de fonction.


Pourtant l'un est un type (défini dans l'espace de nom global, puisque
"class test" est censé y faire référence et non déclarer un nouveau
type) et l'autre une fonction (définie dans la classe B).


Ça fait rien.

En général, en C++, à l'encontre de C, tous les noms sauf des étiquettes
(cibles des goto) se trouvent dans le même espace -- tu ne peux pas
avoir un type et une fonction avec le même nom dans le même espace
référenciel. (Et est-ce que quelqu'un a une idée comment je pourrais
distinguer entre ces deux significations d'espace référentiel ?) Il y a
une exception, un cas spécial qui existe pour la compatibilité C, qui
permet à un nom de classe (pas un nom de type quelconque) à cohabiter
avec le même nom de fonction ou de variable, mais c'est un cas
particulier (on dirait même un hack) pour la compatibilité C, AMHA à
éviter dans le code proprement C++.

Et la
version 3.4 de g++ fait bien la différence puisque je peux écrire :

class test;

struct B { test* test(); }

qui est correct, me semble-t-il.


Je ne crois pas. Correct serait :

class test ;
struct B { class test* test() ; } ;

Mais je ne suis pas 100% certain. Comme j'ai dit, c'est un hack pour de
raisons de compatibilité C, et je ne m'en sers (et ne m'en intéresse)
que dans le cas où ce que j'écris est réelement compatible dans les deux
langages. Quelque chose du genre :

#ifdef __cplusplus
extern "C" {
#endif
struct X { /* ... */ } ;
X* createX() ;
void doSomethingWithX( struct X* ) ;
/* ... */
#ifdef __cplusplus
}
#endif

Mais je pense que c'est une limitation du parseur de g++ 3.3 qui ne
parvient pas à distinguer les deux cas (contexte).


Sauf pour le hack de compatibilité, on ne doit pas avoir le même symbole
dans la même portée avec deux significations différentes.

--
James Kanze GABI Software mailto:
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34