web-dev-qa-db-fra.com

La capture implicite Lambda échoue avec une variable déclarée à partir d'une liaison structurée

Avec le code suivant, j'obtiens une erreur de compilation C2065 'a': undeclared identifier (en utilisant Visual Studio 2017):

[] {
    auto [a, b] = [] {return std::make_Tuple(1, 2); }();
    auto r = [&] {return a; }(); //error C2065
}();

Cependant, le code suivant se compile:

[] {
    int a, b;
    std::tie(a, b) = [] {return std::make_Tuple(1, 2); }();
    auto r = [&] {return a; }();
}();

Je pensais que les deux échantillons étaient équivalents. Est-ce un bug du compilateur ou ai-je raté quelque chose?

41

Core issue 231 a changé la norme afin que les liaisons structurées ne soient jamais des noms de variables, ce qui les rend jamais capturables.

P0588R1 La reformulation du libellé de capture lambda rend cette interdiction explicite:

Si une expression lambda [...] capture une liaison structurée (explicitement ou implicitement), le programme est mal formé.

Notez que ce libellé est censé être un espace réservé tandis que le comité détermine exactement comment de telles captures devraient fonctionner.

Réponse précédente conservée pour des raisons historiques:


Techniquement, cela devrait être compilé, mais il y a un bogue dans la norme ici.

La norme dit que les lambdas ne peuvent capturer que des variables. Et il dit qu'une déclaration de liaison structurée non similaire à Tuple n'introduit pas de variables. Il introduit des noms, mais ces noms ne sont pas des noms de variables.

Une déclaration de liaison structurée de type Tuple, d'autre part, introduit des variables. a et b dans auto [a, b] = std::make_Tuple(1, 2); sont des variables de type référence réelles. Ils peuvent donc être capturés par un lambda.

De toute évidence, ce n'est pas une situation saine, et le comité le sait, donc une solution devrait être trouvée (bien qu'il semble y avoir un certain désaccord sur la façon dont la capture d'une liaison structurée devrait fonctionner).

49
T.C.

Une solution de contournement possible consiste à utiliser une capture lambda avec l'initialiseur. Le code suivant se compile correctement dans Visual Studio 2017 15.5.

[] {
    auto[a, b] = [] {return std::make_Tuple(1, 2); }();
    auto r = [a = a] {return a; }();
}();
15
akarin64