web-dev-qa-db-fra.com

Capture Lambda et paramètre avec le même nom - qui ombrage l'autre? (Clang vs Gcc)

auto foo = "You're using g++!";
auto compiler_detector = [foo](auto foo) { std::puts(foo); };
compiler_detector("You're using clang++!");
  • clang ++ 3.6.0 et les versions plus récentes "Vous utilisez clang ++!" et vous en avertissez capturer foo étant inutilisé.

  • g ++ 4.9.0 et les versions plus récentes "Vous utilisez g ++!" et vous avertissez de la présence de paramètre foo étant inutilisé.

Quel compilateur suit plus précisément la norme C++ ici?

exemple de wandbox

123
Vittorio Romeo

Mise à jour: comme promis par le président Core dans la citation en bas, le code est maintenant mal formé :

Si un identifiant dans un simple-capture apparaît sous la forme déclarator-id d'un paramètre de la lambda-declarator's parameter-declaration-clause, le programme est mal formé.


Il y a quelques temps, il y avait quelques problèmes concernant la recherche de noms dans lambdas. Ils ont été résolus par N2927 :

La nouvelle formulation ne repose plus sur la recherche pour remapper les utilisations des entités capturées. Il nie plus clairement les interprétations selon lesquelles un lambda compound-statement est traité en deux passes ou que tous les noms y compound-statement peut être résolu en un membre du type de fermeture.

La recherche est toujours effectuée dans le contexte de l'expression expression-lambda, jamais "après" la transformation en corps de fonction membre d'un type de fermeture. Voir [expr.prim.lambda]/8 :

Le expression-lambda du instruction-composée donne le fonction-corps ([dcl.fct.def]) de l'opérateur d'appel de fonction, mais aux fins de recherche de nom, […], le instruction-composée est considéré dans le contexte de expression-lambda. [ Exemple:

struct S1 {
  int x, y;
  int operator()(int);
  void f() {
    [=]()->int {
      return operator()(this->x+y);  // equivalent to: S1::operator()(this->x+(*this).y)
                                     // and this has type S1*
    }; 
  }
};

- fin exemple]

(L'exemple montre également clairement que la recherche ne prend pas en compte le membre de capture généré du type de fermeture.)

Le nom foo n'est pas (re) déclaré dans la capture; il est déclaré dans le bloc contenant l'expression lambda. Le paramètre foo est déclaré dans un bloc imbriqué dans ce bloc externe (voir [basic.scope.block]/2 , qui mentionne également explicitement les paramètres lambda). L'ordre de recherche est clairement des blocs intérieurs aux blocs extérieurs . Par conséquent, le paramètre doit être sélectionné, c'est-à-dire que Clang a raison.

Si vous deviez transformer la capture en init-capture, c’est-à-dire foo = "" au lieu de foo, la réponse ne serait pas claire. Ceci est dû au fait que la capture actuellement induit une déclaration dont le "bloc" n'est pas donné. J'ai envoyé un message au président principal à ce sujet, qui a répondu

Ceci est le numéro 2211 (une nouvelle liste de problèmes paraîtra prochainement sur le site open-std.org, malheureusement avec juste des espaces réservés pour un certain nombre de problèmes, dont celui-ci; je travaille dur pour combler ces lacunes avant le Kona réunion à la fin du mois). Le CWG en a discuté lors de notre téléconférence de janvier et a pour objectif de rendre le programme mal formé si un nom de capture est également un nom de paramètre.

64
Columbo

J'essaie de rassembler quelques commentaires sur la question pour vous donner une réponse utile.
Tout d’abord, notez que:

  • Les membres de données non statiques sont déclarés pour le lambda pour chaque variable capturée par copie
  • Dans le cas spécifique, le lambda a un type de fermeture qui a un opérateur d'appel de fonction de modèle en ligne public acceptant un paramètre nommé foo.

Par conséquent, la logique me ferait dire à première vue que le paramètre devrait masquer la variable capturée comme dans:

struct Lambda {
    template<typename T> void operator()(T foo) const { /* ... */ }
    private: decltype(outer_foo) foo{outer_foo};
};

Quoi qu'il en soit, @ n.m. correctement noté que les membres de données non statiques déclarés pour les variables capturées par la copie sont en réalité sans nom Cela étant dit, l'accès au membre de données non nommé est toujours effectué au moyen d'un identifiant (c'est-à-dire foo). Par conséquent, le nom du paramètre de l'opérateur d'appel de fonction devrait toujours (laissez-moi dire) masquer cet identifiant.
Comme cela a été correctement souligné par @ n.m. dans les commentaires à la question:

l'entité capturée d'origine [...] devrait être ombrée normalement conformément aux règles de portée

A cause de cela, je dirais que le bruit est correct.

6
skypjack