Question

Hier, je suis tombé sur un g ++ problème du compilateur (3.4.6) pour le code que je compilent sans problème avec le compilateur Intel (9.0). Voici un extrait de code qui montre ce qui est arrivé:

template<typename A, typename B>
class Foo { };

struct Bar {
   void method ( Foo<int,int> const& stuff = Foo<int,int>() );
};

g ++ erreur du compilateur est:

foo.cpp:5: error: expected `,' or `...' before '>' token
foo.cpp:5: error: wrong number of template arguments (1, should be 2)
foo.cpp:2: error: provided for `template<class A, class B> struct Foo'
foo.cpp:5: error: default argument missing for parameter 2 of `void Bar::method(const Foo<int, int>&, int)'

Apparemment, l'argument par défaut ne sont pas acceptées lors de l'écriture de cette façon, et le compilateur suppose que, au lieu du deuxième argument de modèle un nouvel argument de fonction est spécifiée, pour laquelle il attend alors une valeur par défaut parce que l'argument stuff a un . Je peux aider le compilateur en créant un typedef, puis tout compile bien:

template<typename A, typename B>
class Foo { };

struct Bar {
   typedef Foo<int,int> FooType;
   void method ( FooType const& stuff = FooType() );
};

Je peux résoudre mon problème, mais je ne comprends pas ce qui se passe. Est-ce que je manque un C ++ (modèle?) Caractéristique de la langue ici et que je fais quelque chose de mal, ou est le compilateur g ++ tort de ne pas accepter le premier morceau de code?

Note BTW que cette compile aussi ...

template<typename A, typename B>
class Foo { };

void method ( Foo<int,int> const& stuff = Foo<int,int>() );
Était-ce utile?

La solution

Je ne suis pas sûr que ce soit un bug en g ++ (depuis la version 4.2.4). Le code passe maintenant en g ++ 4.4 (voir Mise à jour ci-dessous). Pour cette compilation de code pour les autres versions de compilateurs, vous pouvez ajouter un ensemble de parenthèses autour de l'argument par défaut:

template<typename A, typename B>
class Foo { };

struct Bar {
  void method ( Foo<int,int> const& stuff = ( Foo<int,int>() ) );
};

OMI, ces parenthèses sont nécessaires car il y a une exigence supplémentaire que l'argument par défaut peut se référer à un membre de la classe qui peut être déclaré plus tard dans le corps de la classe:

struct Bar {
  void method ( int i = j);  // 'j' not declared yet
  static const int j = 0;
};

Le code ci-dessus est légal, et quand la déclaration de « méthode » est en cours d'analyse le membre « j » n'a pas encore été vu. Le compilateur ne peut donc analyser l'argument par défaut en utilisant syntaxe vérifier que (ie. Entre parenthèses correspondant et les virgules). Lorsque g ++ est votre déclaration originale analyse pas, ce qu'il voit en fait est le suivant:

void method ( Foo<int,int> const& stuff = Foo<int // Arg 1 with dflt.
              , int>() );                         // Arg 2 - syntax error

Ajout de l'ensemble supplémentaire de parenthèses veille à ce que l'argument par défaut est géré correctement.

Le cas suivant montre un exemple où g ++ réussit mais encore Comeau génère une erreur de syntaxe:

template<int J, int K>
class Foo { };

struct Bar {
  void method ( Foo<0, 0> const & i = ( Foo<j, k> () ) );
  static const int j = 0;
  static const int k = 0;
};

EDIT:

En réponse au commentaire: « Vous pourriez tout aussi bien avoir un appel de fonction avec de multiples arguments là-bas », la raison pour que cela ne pose pas de problème est que les années virgule à l'intérieur de l'appel de fonction sont entourés entre parenthèses:

int foo (int, int, int);
struct Bar {
  void method ( int j =
                    foo (0, 0, 0) ); // Comma's here are inside ( )
};

Il est donc possible d'analyser cela en utilisant la syntaxe de l'expression seulement. En C ++, tout « ( » doit être associé à « ) » et donc c'est facile à analyser. La raison de ce problème ici est que « < » n'a pas besoin d'être adapté, car il est surchargé en C ++ et ne peut donc être inférieur à l'opérateur ou le début d'une liste d'arguments de modèle. L'exemple suivant montre où « < » est utilisé dans l'argument par défaut et implique moins que l'opérateur:

template<int I, int J>
class Foo { };

struct Bar {
  template <typename T> struct Y { };

  void method ( ::Foo<0,0> const& stuff = Foo<10 , Y < int >  = Y<int>() );

  struct X {
    ::Foo<0, 0> operator< (int);
  };

  static X Foo;
};

Le ci-dessus « Foo <10 » est un appel à l'opérateur « < » défini dans « X », pas le début de la liste des arguments de modèle. Encore une fois, Comeau génère des erreurs de syntaxe du code ci-dessus et g ++ (y compris 3.2.3) analyser ce correctement.

Pour votre information, les références appropriées sont une note 8.3.6 / 5:

  

[Note: dans les déclarations de fonctions membres, les noms dans les expressions d'argument par défaut sont considérés   comme décrit dans 3.4.1 ...

puis dans 3.4.1 / 8

  

Un nom utilisé dans la définition d'une fonction de membre (9.3) de la classe X suivant la declaratorid29 de la fonction)   doit être déclaré dans l'une des façons suivantes:

...

  

- est membre de la classe X ou être membre d'une classe de base de X (10.2) ou

Cette balle ici, est la partie qui force le compilateur de rechercher « retard » pour le sens de l'argument par défaut jusqu'à ce que tous les membres de la classe ont été déclarés.

Comme l'a souligné « russe occupée », g ++ 4.4 est maintenant en mesure d'analyser tous ces exemples. Cependant, jusqu'à ce que le DR a été adressée par le comité des normes C ++ Je ne suis pas encore prêt à appeler cela un « bug ». Je crois que la parenthèse extra long terme sera nécessaire pour assurer la portabilité vers d'autres compilateurs / outils (et peut-être même des versions futures de g ++).

Il a été mon expérience que le standard C ++ ne dicte pas que les fournisseurs de compilateur devraient utiliser les mêmes technologies de l'analyseur et ils ne peuvent pas attendre à ce que toutes les technologies sont tout aussi puissantes. Par conséquent, les exigences parsing ne nécessitent généralement pas que les fournisseurs exécutent des exploits surhumains. Pour illustrer cela considérer les deux exemples suivants:

typedef T::TYPE TYPE;
T::TYPE t;

Si 'T' est dépendante, alors chaque contexte donné 'TYPE' doit un typename, mais la norme exige toujours le typename mot-clé. Ces exemples ne sont pas ambigus et ne peuvent signifier qu'une seule chose, mais la norme (afin de permettre à toutes les technologies de l'analyseur) nécessitent encores typename mot-clé.

Il est possible que la RD peut être traitée de telle sorte qu'un compilateur qui ne parvient pas à analyser ces exemples sera toujours « conforme au standard » aussi longtemps que la parenthèse supplémentaire permet le code à analyser.

Autres conseils

Ceci est un bug connu gcc. Il a été corrigé dans gcc-4.4, qui compile la source originale très bien.

On dirait un bug du compilateur. Je l'ai essayé sur le compilateur xlC d'IBM V7.0 (que j'ai trouvé pour être plus conforme à la norme que gcc) et il compile bien.

template <bool> class A {};
typedef A<static_cast<bool>(1>0)> B;//buggy
int main() { B b; }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top