2017-06-25 9 views
8

Mam następujący (uproszczony) kod w moim obecnym projekcie:C++ lambda'this' wskaźnik unieważnianie po operacji przesuwania

#include <iostream> 
#include <string> 
#include <functional> 
#include <vector> 


class Test{ 

public: 

    Test() = default; 
    Test(const Test& other) = delete; 
    Test& operator=(const Test& other) = delete; 
    Test(Test&& other) = default; 
    Test& operator=(Test&& other) = default; 



    void setFunction(){ 
     lambda = [this](){ 
      a = 2; 
     }; 
    } 

    int callAndReturn(){ 
     lambda(); 
     return a; 
    } 

private: 
    std::function<void()> lambda; 
    int a = 50; 
}; 


int main() 
{ 
    Test t; 
    t.setFunction(); 
    std::vector<Test> elements; 
    elements.push_back(std::move(t)); 
    std::cout << elements[0].callAndReturn() << std::endl; 
} 

Kiedy uruchamiam go, wartość 50 jest drukowany zamiast oczekiwanej wartości 2. Przypuszczam, że tak się dzieje, ponieważ funkcja lambda przechwytuje bieżący wskaźnik this. Po operacji przenoszenia wskaźnik this zmienia się, a funkcja zapisuje na niewłaściwym a.

Teraz moje pytanie brzmi: Czy istnieje sposób na zmianę przechwyconego odniesienia lambda do nowego Test, aby wydrukowano wartość 2?

Odpowiedz

4

Rozwiązaniem nie jest uchwycenie w ogóle this. Zamiast tego zmień przechwycony typ funkcji, aby go zaakceptować. I użyj wskaźnika do elementu (przechwyconego przez wartość) dla pośredniego dostępu do a.

std::function<void(Test*)> lambda; 

void setFunction(){ 
    auto a = &Test::a; 
    lambda = [=](Test *t){ 
     (t->*a) = 2; 
    }; 
} 

int callAndReturn(){ 
    lambda(this); 
    return a; 
} 

Live Example


Jak zauważył Galik, jeśli tylko potrzebują dostępu do pojedynczego elementu zakodowane, to nawet nie trzeba, że ​​wskaźnik do członka. Zatem lambda może być pozbawiona przechwytywania:

void setFunction(){ 
    lambda = [](Test *t){ 
     t->a = 2; 
    }; 
} 
+2

Nie możesz po prostu zrobić 't-> a = 2;' i zapomnieć o wskaźniku elementu? – Galik

+0

@Galik - Nie wiem dlaczego, ale odniosłem wrażenie, że OP chciał, aby członek nie był na sztywno. Dodam, że oczywiście z uwagi na to, że lambda bez wychwytywania jest jeszcze prostsza, jak zauważyliście. – StoryTeller

+0

Wydaje mi się, że podstawowym problemem jest przechwytywanie wskaźnika lambda "this", który nie jest aktualizowany przez * move *. Podając bieżący wskaźnik 'this' jako parametr, lambda zawsze działa na poprawnym obiekcie. – Galik