55 votes

Capturer une variable statique par référence dans une lambda C++11

Question principale

Je tente de compiler le code suivant avec GCC 4.7.2:

#include 

int foo() {
    static int bar;
    return [&bar] () { return bar++; } (); // lambda qui capture par référence
}

int main (int argc, char* argv[]) {
    std::cout << foo() << std::endl;
    return 0;
}

Et il semble que ça ne se passe pas bien, car le résultat est le suivant:

$p2.cpp: Lors de la fonction ‘int foo()’:
$p2.cpp:6:14: avertissement: capture de la variable ‘bar’ avec une durée de vie non automatique [activé par défaut]
$p2.cpp:4:16: remarque: ‘int bar’ déclaré ici

Donc, ma première question serait:

S'agit-il d'un échec de GCC, ou le code n'est-il pas légitime en C++11? Est-ce corrigé dans une version récente de GCC?

Utiliser l'astuce dans une fabrique de shared_ptr

J'envisage de construire un artefact basé sur ce principe mais en utilisant une variable statique non littérale. Cet artefact est censé être une fabrique d'objets shared_ptr< T >, qui évite la création de nouveaux objets T lorsque vous avez simplement besoin d'un conteneur shared_ptr en double pour la même instance.

Cet artefact ressemblerait à ceci:

std::shared_ptr create(std::string name) {
    static std::unordered_map> registry;

    if (auto it = registry.find(name) != registry.end())
        return registry[name].lock();

    auto b = std::shared_ptr(
        new Foo(name), 
        [®istry] (Foo* p) {
            registry.erase(p->getName());
            delete p;
        });

    registry.emplace(name,b);
    return b;
}

D'après ce que je sais, si le problème GCC discuté précédemment ne pose pas de problème en termes de conformité C++11, cet artefact ne devrait pas non plus poser de problème. La seule chose à prendre en compte en utilisant cette astuce est de ne pas définir l'objet shared_ptr< T > résultant sur un objet global qui pourrait être détruit après la variable statique.

Suis-je dans le vrai à ce sujet?

113voto

Kevin Ballard Points 88866

Pourquoi essayez-vous même de capturer bar? C'est statique. Vous n'avez pas besoin de le capturer du tout. Seules les variables automatiques ont besoin d'être capturées. Clang lance une erreur dure sur votre code, pas juste un avertissement. Et si vous supprimez simplement le &bar de la capture de votre lambda, alors le code fonctionne parfaitement.

#include <iostream>

int foo() {
    static int bar;
    return [] () { return bar++; } (); // lambda capturant par référence
}

int main (int argc, char* argv[]) {
    std::cout << foo() << std::endl;
    std::cout << foo() << std::endl;
    std::cout << foo() << std::endl;
    return 0;
}

imprime

0
1
2

16voto

Donnie Points 17312

Conformément à la norme, vous ne pouvez capturer que des variables avec une durée de stockage automatique (ou this, qui est mentionné comme capturable de manière explicite).

Donc, non, vous ne pouvez pas le faire selon la norme (ou, pour répondre à votre première question, ce n'est pas valide en C++ 11 et ce n'est pas un bogue du compilateur)

 

5.1.1/2 Un nom dans la capture du lambda doit être dans le contexte de l'expression lambda, et doit être this ou faire référence à une variable locale ou à une référence avec une durée de stockage automatique.

EDIT: Et, comme Kevin l'a mentionné, vous n'avez même pas besoin de capturer un local static de toute façon.

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