Question

J'ai une classe qui a un public std::function membre comme ceci:

class B
{
public:
    B(std::function<void(void)> _func = NULL) : m_function(_func) { }
    std::function<void()> m_function;
};

J'ai une classe X avec une fonction membre SomeFunction:

class X
{
public:
    void SomeFunction(const std::string & _s)
    {
        //...
    }
};

J'ai aussi une classe A qui retourne un objet de type B, comme ceci:

class A
{
private:
    X x;
public:
    B GetObjectB()
    {
        std::string local = "abc";
        return B(([&]() -> void { x->SomeFunction(local); }))
    }
};

J'ai ensuite exécuter

A a;
B b = a.GetObjectB();
b.m_function();

Le code fonctionne très bien jusqu'à ce que la fonction SomeFunction est appelé, et les arguments d'entrée _s n'a pas une valeur bien avoir passé local comme argument d'entrée.

Ce que je fais mal ici?

Était-ce utile?

La solution

Vous dites à votre lambda de capture par défaut par référence ([&]), et c'est la capture de la référence à local c'est sur la pile.Comme une pile variable, il est libéré lorsque le champ d'application des sorties, de sorte que le retour de l'objet my_function a capturé une balançant de référence.Essayez [=].

Autres conseils

Ce morceau de code

std::string local="abc";
return B(([&]()->void{x->SomeFunction(local);}))

capture toutes les variables utilisées par référence, et renvoie la fonction.Entre la capture de variables est l' local variable local, dont la durée de vie se termine lorsque la portée du sort, c'est à direlorsque, de retour de la fonction.De sorte que le local la variable n'existe plus lorsque le lambda sera exécuté plus tard, ce qui signifie que la capture de référence n'est pas valide ("bancales de référence").

Citant http://en.cppreference.com/w/cpp/language/lambda:

En balançant les références

Si une entité est capturé par référence, implicitement ou explicitement, et l'appel de la fonction de l'opérateur de la fermeture de l'objet est appelée après l' entité de la vie a pris fin, un comportement indéfini se produit.Le C++ les fermetures de ne pas prolonger la durée de vie de la capture des références.

Ce paragraphe indique que, dans d'autres langues avec le soutien de fermeture (par ex.en JavaScript), la durée de vie des variables capturées par référence sont étendu, qui n'est pas le cas en C++.

Le même problème existe pour la variable x qui est une variable membre de la classe A la fonction a été appelée sur.Alors que vous n'avez probablement pas un problème de code dans votre test, cela peut devenir un problème, trop:

B b;

// some other context
{
    A a;
    b = a.GetObjectB();
}

// The lambda previously returned from a.GetObjectB() is now
// stored in b. But it still refers to the now dead a.x!

b.m_function();    // BOOM!

Une solution facile est de simplement pas compte de toutes les variables par référence, mais par valeur, de sorte que la variable est copiée dans le lambda exemple:

std::string local="abc";
return B(([=]()->void{x->SomeFunction(local);}))

Solutions de contournement possibles lorsque vous ne souhaitez pas copier la valeur sont discutés dans ma réponse à une question très semblable: https://stackoverflow.com/a/20928847/592323.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top