126 votes

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 plus récent imprimer "Vous êtes à l'aide de clang++!" et d'avertir au sujet de la capture foo être utilisés.

  • g++ 4.9.0 et plus récent imprimer "Vous êtes à l'aide de g++!" et signaler le paramètre foo être utilisés.

Ce compilateur est plus précisément suivant la Norme C++ ici?

wandbox exemple

66voto

Columbo Points 11661

Mise à jour: comme promis par le président dans le bas du devis, le code est maintenant mal formé:

Si un identificateur dans une simple capture apparaît comme la déclaration de l'id d'un paramètre de la lambda-déclaration de's le paramètre-déclaration de la clause de sauvegarde, le programme est mal formé.


Il y avait quelques questions concernant la recherche d'un nom dans les lambdas il y a un moment. Ils ont été résolus par N2927:

Le nouveau libellé ne s'appuie plus sur la recherche pour remapper les utilisations de la capture des entités. Plus clairement rejette les interprétations qu'un lambda est composé déclaration est traitée en deux passes ou que les noms composés-déclaration peut se résoudre à un membre de la fermeture type.

Recherche se fait toujours dans le contexte de la lambda-expression, jamais "après" la transformation d'une fermeture du type de fonction de membre du corps. Voir [expr.prim.lambda]/8:

La lambda-expression' composé-déclaration des rendements de la fonction-corps ([dcl.fct.def]) de l'appel de la fonction de l'opérateur, mais pour les fins de la recherche d'un nom, [...], le composé déclaration est considérée dans le contexte de la lambda-expression. [ 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 de l'exemple ]

(L'exemple est tout aussi évident que la recherche n'est pas en quelque sorte considérer la capture généré par le membre de la fermeture de leur type.)

Le nom de l' foo n'est pas (re)déclarés dans le capturer; il est déclaré dans le bloc englobant l'expression lambda. Le paramètre foo est déclarée dans un bloc qui est imbriquée dans le bloc externe (voir [de base.la portée.bloc]/2, qui mentionne aussi explicitement lambda paramètres). L'ordre de recherche est clairement à partir de l'intérieur vers l'extérieur des blocs. D'où le paramètre doit être sélectionné, c'est, Clang est droit.

Si vous effectuez la capture d'un init-capture, c'est à dire foo = "" au lieu de foo, la réponse ne serait pas clair. C'est parce que la capture maintenant effectivement induit une déclaration dont le "bloc" n'est pas donné. Je me messaged la base de la chaire de ce, qui a répondu

C'est la question 2211 (une nouvelle liste des questions qui apparaissent sur l'open-std.org site peu de temps, malheureusement, avec seulement des espaces réservés pour un certain nombre de questions, dont celle-ci; je suis en train de travailler dur pour combler ces lacunes avant le Kona réunion à la fin du mois). GTC discuté lors de notre téléconférence de janvier, et la direction est de faire le programme mal formé si une capture nom est aussi un nom de paramètre.

6voto

skypjack Points 5516

Je suis en train de pack ainsi que quelques commentaires à la question, pour vous donner une réponse utile.
Tout d'abord, notez que:

  • Non les données membres statiques sont déclarées pour le lambda pour chaque copie capturée de la variable
  • Dans le cas particulier, le lambda est une fermeture de type qui a un public inline modèle de la fonction appeler l'opérateur d'accepter un paramètre nommé foo

Donc la logique voudrait me faire dire au premier coup d'oeil que le paramètre devrait l'ombre de la capture de variable, comme dans:

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

De toute façon, @n.m. note bien que les non-membres de données statiques déclaré pour la copie capturée de variables sont en réalité sans nom. Cela étant dit, sans nom de membre de données est toujours accessible par le biais d'un identifiant (c'est - foo). Par conséquent, le nom du paramètre de l'appel de la fonction opérateur doit toujours (permettez-moi de le dire) de l'ombre que l'identificateur.
Comme souligné à juste titre par @n.m. dans les commentaires à la question:

l'original capturé entité [...] devrait être l'ombre normalement, selon les règles d'étendue

À cause de cela, je dirais que clang est droit.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X