72 votes

Lambdas C++ : Différence entre "mutable" et capture par référence

En C++, vous pouvez déclarer des lambdas par exemple comme ceci :

int x = 5;
auto a = [=]() mutable { ++x; std::cout << x << '\n'; };
auto b = [&]()         { ++x; std::cout << x << '\n'; };

Les deux me permettent de modifier x alors quelle est la différence ?

96voto

phresnel Points 20082

Ce qui se passe

Le premier ne modifiera que sa propre copie de x et laisser l'extérieur x inchangé. La seconde modifiera le à l'extérieur de x .

Ajoutez une déclaration d'impression après avoir essayé chacune d'elles :

a();
std::cout << x << "----\n";
b();
std::cout << x << '\n';

Il est prévu de l'imprimer :

6
5
----
6
6

Pourquoi

Il peut être utile de considérer que lambda

[...] Les expressions fournissent un moyen concis de créer des objets fonctionnels simples.

(voir [expr.prim.lambda] de la norme)

Ils ont

[...] un opérateur public d'appel de fonction inline [...]

qui est déclaré comme un const mais seulement

[...] si et seulement si l'expression lambda clause de déclaration des paramètres n'est pas suivi par mutable

Vous pouvez penser à comme si

    int x = 5;
    auto a = [=]() mutable { ++x; std::cout << x << '\n'; };

==>

    int x = 5;

    class __lambda_a {
        int x;
    public:
        __lambda_a () : x($lookup-one-outer$::x) {}
        inline void operator() { ++x; std::cout << x << '\n'; }     
    } a;

y

    auto b = [&]()         { ++x; std::cout << x << '\n'; };

==>

    int x = 5;

    class __lambda_b {
        int &x;
    public:
        __lambda_b() : x($lookup-one-outer$::x) {}
        inline void operator() const { ++x; std::cout << x << '\n'; }         
        //                     ^^^^^
    } b;

Q : Mais si c'est un const pourquoi je peux encore modifier x ?

A : Vous ne faites que changer l'extérieur x . Le propre lambda x est une référence, et l'opération ++x ne modifie pas la référence mais la valeur référencée .

Cela fonctionne parce qu'en C++, la constance d'un pointeur/référence ne change pas la constance de la pointe/référence vue à travers lui.

1 votes

Sympathique et minutieux. Mais je pense que vous avez a certains endroits que vous vouliez dire b . Et vos objets de type classe anonyme n'ont pas d'initialisateurs. Il faudrait peut-être expliquer que nous prétendons lambda_a() est un constructeur bien que la classe n'ait pas de nom.

0 votes

@aschepler : Merci pour les suggestions :) J'ai changé pour des noms réservés.

0 votes

Pourriez-vous nous en dire un peu plus sur la constance ? Je n'ai pas eu une image claire. Je pense qu'expliquer ce qui se passerait si le 'const' n'était pas présent à cet endroit rendrait les choses plus faciles.

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