76 votes

Pourquoi est-il parfois nécessaire de ne pas capturer une variable const dans un lambda?

Considérons l'exemple suivant:

#include <cstdlib>

int main() {
    const int m = 42;
    [] { m; }(); // OK

    const int n = std::rand();
    [] { n; }(); // error: 'n' is not captured
}

Pourquoi ai-je besoin pour capturer n dans le deuxième lambda, mais pas m dans la première lambda? J'ai vérifié la section 5.1.2 (expressions Lambda) dans le C++14 standard, mais j'ai été incapable de trouver une raison. Pouvez-vous m'indiquer un paragraphe dans lequel c'est expliqué?

Mise à jour: j'ai observé ce comportement avec GCC 6.3.1 et 7 (le tronc). Clang 4.0 et 5 (le tronc) échoue avec une erreur dans les deux cas (variable 'm' cannot be implicitly captured in a lambda with no capture-default specified).

56voto

Matt McNabb Points 14273

Pour un lambda au bloc de portée, les variables répondant à certains critères dans le large spectre peut être utilisé de façon limitée à l'intérieur de la lambda, même s'ils ne sont pas capturés.

Grosso modo, pour atteindre la portée comprend toute variable locale à la fonction contenant le lambda, qui serait portée au point de la lambda a été défini. Donc, cela inclut m et n dans les exemples ci-dessus.

"À certains critères" et "limitée", sont spécifiquement (comme en C++14):

  • À l'intérieur de la lambda, la variable ne doit pas être odr-utilisé, ce qui signifie qu'il ne doit pas subir toute opération, à l'exception:
    • apparaissant comme un jeté de la valeur de l'expression (m; est l'un de ceux-ci), ou
    • avoir sa valeur de récupération.
  • La variable doit être:
    • Un const, non-volatile entier ou enum dont l'initialiseur était une expression constante, ou
    • Un constexpr, non-volatile variable (ou un sous-objet de ce genre)

Les références à C++14: [expr.const]/2.7, [de base.def.odr]/3 (première phrase), [expr.prim.lambda]/12, [expr.prim.lambda]/10.

La raison d'être de ces règles, comme suggéré par d'autres observations/réponses, c'est que le compilateur a besoin pour être en mesure de "synthétiser" un non-capture lambda comme une fonction indépendante du bloc (puisque de telles choses peuvent être convertis à un pointeur de fonction); il peut le faire en dépit de référence à la variable si elle sait que la variable aura toujours la même valeur, ou il peut répéter la procédure pour l'obtention de la valeur de la variable indépendante du contexte. Mais il ne peut pas le faire si la variable peut varier de temps à autre, ou si la variable adresse est nécessaire par exemple.


Dans votre code, n a été initialisée par une non-expression constante. Par conséquent, n ne peut pas être utilisé dans un lambda sans être capturé.

m a été initialisée par une expression constante 42, de sorte qu'il ne répondait pas à la "certains critères". Un jeté de la valeur de l'expression n'a pas d'odr-utiliser l'expression, alors m; peuvent être utilisés sans l' m d'être capturé. gcc est correct.


Je dirais que la différence entre les deux compilateurs, c'est que clang considère m; d'odr-utiliser m, mais gcc n'a pas. La première phrase de l' [de base.def.odr]/3 est assez compliqué:

Une variable x dont le nom apparaît comme potentiellement-de l'expression évaluée ex est odr-utilisée par ex moins que l'application de la lvalue-à-rvalue conversion en x donne une expression constante qui n'a pas invoquer la non-trivial fonctions et, si x est un objet, ex est un élément de l'ensemble des résultats possibles d'une expression e, où la lvalue-à-rvalue de conversion est appliqué à l' eou e est un jeté de la valeur de l'expression.

mais à la lecture de près, il fait spécifiquement mention qu'un rebut de la valeur de l'expression n'a pas d' odr-utilisation de l'expression.

C++11 de la version de [base.def.odr] à l'origine, n'incluent pas de la jetée de la valeur de l'expression, de ce bruit de comportement serait correct dans la publication de C++11. Cependant, le texte qui s'affiche en C++14 a été accepté comme un Défaut à l'encontre de C++11 (Numéro 712), de sorte que les compilateurs doivent mettre à jour leur comportement, même en C++11 mode.

34voto

Beginner Points 2743

Son parce que c'est une expression constante, le compilateur traite est comme si c'était [] { 42; }();

La règle dans [expr.prim.lambda] est:

Si une lambda-expression ou d'une instanciation de l'appel de la fonction opérateur de modèle générique d'une lambda odr-usages (3.2) cette ou un variable automatique de la durée de stockage à partir de son large spectre, cette entité doit être capturé par le lambda-expression.

Ici, une citation de la norme debase.def.odr]:

Une variable x dont le nom apparaît comme un élément potentiellement-de l'expression évaluée ex est de rll-utilisé, à moins que l'application de la lvalue-à-rvalue conversion de x donne une expression constante (...) ou e est un jeté de la valeur de l'expression.

(Supprimé pas partie tellement importante pour le garder court)

Ma compréhension simple est: le compilateur sait qu' m est constante à la compilation, alors que n va changer au moment de l'exécution et, par conséquent, n a à être capturé. n serait odr-utilisé, parce que vous avez à jeter un oeil à ce qui est à l'intérieur d' n au moment de l'exécution. En d'autres termes, le fait qu ' "il ne peut être qu'une" définition de l' n est pertinent.

C'est à partir d'un commentaire par M. M:

m est une constante de l'expression parce que c'est un const variable automatique avec l'expression constante de l'initialiseur, mais ce n est pas une constante l'expression en raison de son initialiseur n'était pas une expression constante. Cette est couvert dans [expr.const]/2.7. L'expression constante est pas ODR-utilisé, selon la première phrase de l' [de base.def.odr]/3

Voir ici pour une démo.

2voto

user2079303 Points 4916

EDIT: La version précédente de ma réponse était fausse. Du débutant est correct, ici, est de normalisation pertinents citation:

[de base.def.odr]

  1. Une variable x dont le nom apparaît comme potentiellement-de l'expression évaluée ex est de rll-utilisés par ex à moins que l'application de la lvalue-à-rvalue conversion de x donne une expression constante qui n'a pas invoquer la non-trivial fonctions et, si x est un objet, un ex est un élément de l'ensemble des résultats possibles d'une expression e, où la lvalue-à-rvalue de conversion est appliqué à e, ou e est un jeté de la valeur de l'expression. ...

Depuis m est une expression constante, il n'est pas odr-utilisé et n'a donc pas besoin d'être capturé.

Il semble que résonne le comportement n'est pas conforme à la norme.

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