g ++ n’aime pas la méthode template chaining sur template var?

J’essaie de comstackr avec g ++ du code précédemment développé sous Visual C ++ 2008 Express Edition , et il semble que g ++ ne me permette pas d’appeler une méthode de modèle sur une référence renvoyée par une méthode d’une variable de modèle. J’ai pu réduire le problème au code suivant:

class Inner { public: template T get() const { return static_cast(value_); }; private: int value_; }; class Outer { public: Inner const& get_inner() { return inner_; }; private: Inner inner_; }; template int do_outer(T& val) { return val.get_inner().get(); } int main() { Outer outer; do_outer(outer); return 0; } 

Le code se comstack bien sous le compilateur de Microsoft, mais g ++ génère une erreur:

 $ g++ -c main.cpp main.cpp: In function 'int do_outer(T&)': main.cpp:24: error: expected primary-expression before 'int' main.cpp:24: error: expected ';' before 'int' main.cpp:24: error: expected unqualified-id before '>' token 

où la ligne 24 fait référence à return val.get_inner().get(); .

Si je fais de do_outer une méthode normale recevant une référence Outer le code est compilé. Rendre Inner::get() une méthode normale fonctionne également. Et obliger Inner::get() retourner void et à recevoir un paramètre de modèle fonctionne également parce que le spécificateur int ci-dessous devient inutile, c’est-à-dire:

 class Inner { public: template void get(T& val) const { val = static_cast(value_); }; private: int value_; }; ... template int do_outer(T& val) { int i; val.get_inner().get(i); return i; } ... 

(g ++ ne se plaint pas du code ci-dessus.)

Maintenant je suis à court d’idées. Quel est le problème? Existe-t-il un problème avec gcc / g ++? Existe-t-il un problème de conformité avec mon code?

Le compilateur que j’utilise est:

 $ g++ --version g++ (Ubuntu 4.3.3-5ubuntu4) 4.3.3 

Pourriez-vous essayer avec?

 template int do_outer(T& val) { return val.get_inner().template get(); } 

Je n’ai pas access à gcc atm, mais j’ai eu des problèmes similaires et l’ajout du mot-clé template a toujours permis de les résoudre. Et cela fonctionne aussi dans le VS.

Juste pour vous expliquer pourquoi le mot-clé template est nécessaire:

 template int do_outer(T& val) { int i; val.get_inner().get(i); return i; } 

Lorsque le compilateur voit cette fonction, il ne sait pas quel est le type de val . Il parsing donc la ligne val.get_inner().get(i) comme suit:

1: val .

Le compilateur voit le . et donc peut supposer que ‘val’ a un type de classe et que l’identifiant suivant est le nom d’un object membre ou d’une fonction.

2. val . get_inner ( val . get_inner (

get_inner est le nom du membre, puis le compilateur voit le ( . La seule possibilité est que get_inner est un nom de fonction et qu’il s’agit donc d’un appel de fonction. Il parsing ensuite les parameters jusqu’à ce qu’il trouve la fermeture ) .

3. val . get_inner () . val . get_inner () .

En ce qui concerne la première étape, il sait maintenant que le retour de get_inner doit être un type de classe afin de savoir que l’identificateur suivant est un object membre ou une fonction.

4. val . get_inner () . get < val . get_inner () . get <

Alors, que peut signifier le < éventuellement? Bien sûr, c'est le début des arguments de template ... ou peut-être que l'opérateur est moins que opérateur?

Nous soaps que get ne peut être qu'un object ou une fonction. S'il s'agit d'un object, le < est parfaitement logique en tant qu'opérateur inférieur à. De plus, la norme indique plus ou moins que ce n'est que là où le nom précédant le < est un template-name qu'il traitera le < comme argument de modèle (14.2 / 3):

Une fois que name lookup (3.4) a découvert qu'un nom est un nom de modèle, si ce nom est suivi d'un < , le < est toujours considéré comme le début d'une liste de modèles d'arguments et jamais comme un nom suivi du signe moins. que l'opérateur.

Dans ce cas, le compilateur n'a aucune idée du type de l'expression val.get_inner() et ne peut donc pas get recherche. Il suppose alors plus ou moins qu'il s'agit d'un object membre et non d'un nom de modèle. '<' est considéré comme l'opérateur inférieur à et le compilateur finit par vérifier si get est inférieur à int - d'où l'erreur.

Alors, pourquoi les correctifs fonctionnent-ils?

Ajout du mot clé de template

Nous disons littéralement au compilateur que la commande get est un nom de modèle < opérateur < est donc traité comme le début d'une liste d'arguments de modèles.

Supprimer les arguments de template

Quand do_outer n'a pas les arguments du template, ie: val . get_inner () . get ( val . get_inner () . get ( val . get_inner () . get ( le compilateur s'attend à ce que le membre get soit un object ou une fonction. Le ( dissocie les deux et le nom est traité comme une fonction. La déduction d'argument de modèle ultérieur détermine ensuite le type du paramètre de modèle.

Je ne peux pas prétendre être l’une des 10 personnes de la planète qui comprend parfaitement les modèles C ++, mais ce que vous faites ici me convient bien. (Il échoue avec GCC 4.4.1 avec la même erreur, BTW).

Changer do_outer en

 const Inner& inner = val.get_inner(); return inner.get(); 

fonctionne avec GCC et probablement aussi avec Visual C ++.

Vous pourriez envisager de déposer un bogue avec GCC; soit ils le répareront, soit ce sera fermé comme INVALID et, dans le processus, quelqu’un expliquera, espérons-le, pourquoi ce que vous faites n’est pas un code valide.

Une nouvelle mise à jour et AHA: Il s’avère que ce n’est pas un code valide, GCC ne fait que donner un horrible message d’erreur. Intel C ++ génère le message d’erreur (réellement utile!):

 template.cpp(24): error: type name is not allowed return val.get_inner().get(); 

Ce qui m’a fait comprendre le problème. Changer de do_inner en

  return val.get_inner().template get(); 

le code est accepté à la fois par ICC et GCC.