2012-12-04 8 views
9

Próbuję przechwycić obiekt const za pomocą kopii w (zmiennym) lambda. Mój kompilator jednak skarży się, że przechwycony obiekt jest const.Skopiowany obiekt const w zamknięciu lambda niezmiennym

Czy nie można skopiować obiektu jako niestałego?

struct Foo 
{ 
    Foo(){} 
    void Func(){} 
}; 

int main() 
{ 
    const Foo foo; 
    [foo]() mutable { foo.Func(); }; 
} 

Kompilacja z g ++ 4.7.2:

testcase.cpp: In lambda function: 
testcase.cpp:10:29: error: no matching function for call to ‘Foo::Func() const’ 
testcase.cpp:10:29: note: candidate is: 
testcase.cpp:4:7: note: void Foo::Func() <near match> 
testcase.cpp:4:7: note: no known conversion for implicit ‘this’ parameter from ‘const Foo*’ to ‘Foo*’ 

kompilacji z brzękiem ++ 3.1:

testcase.cpp:10:20: error: member function 'Func' not viable: 'this' argument has type 'const Foo', but function is not marked const 
    std::async([foo]() mutable { foo.Func(); }); 

Dokument Standard (lub raczej projekt ...) definiuje w 5.1.2.14 "Typ [...] jest typem odpowiedniego przechwyconego bytu", więc myślę, że zawierałby specyfikatory cv.
Nie wydaje się to jednak intuicyjne.

+0

Można jawnie skopiować wewnątrz ciała lambda, ale myślę, że nie tego szukacie. Można oczywiście wziąć odwołanie do wartości r jako parametr podany w C++ 11. – CashCow

+0

Jakie jest pytanie? – chill

+0

@chill To jest pytanie: dlaczego skopiowany obiekt foo wewnątrz lambda jest const? –

Odpowiedz

6

pierwsze, typ wyrażenia lambda, która ma przechwytywania, jest typem klasy (5.1.2 wyrażenia lambda [expr.prim.lambda] # 3)

Ten typ ma operator() który jest domyślnie const, chyba że mutable jest używane w wyrażeniu lambda ([expr.prim.lambda] # 5)

Następnie, dla każdej jednostki uchwyconej jako kopia, nienazwany członek jest deklarowany w typie zamknięcia. [expr.prim.lambda] # 14]

Jeśli jawnie zbudujesz (najczęściej) odpowiednik typu przechwytywania, wszystko będzie naturalnie wynikać ze zwykłej semantyki dla klas, typów const-const i stałych funkcji składowych.

przykład:

struct S 
{ 
    void f(); 
    void fc() const; 
}; 

void g() 
{ 
    S s0; 

    // [s0]() { s0.f(); }; // error, operator() is const 
    [s0]() { s0.fc(); }; // OK, operator() is const, S::fc is const 

    [s0]() mutable { s0.f(); }; 
    [s0]() mutable { s0.fc(); }; 

    const S s1; 

    // [s1]() { s1.f(); }; // error, s1 is const, no matter if operator() is const 
    [s1]() { s1.fc(); }; 

    // [s1]() mutable { s1.f(); }; // error, s1 is const, no matter if operator() is const 
    [s1]() mutable { s1.fc(); }; 
} 

Chyba zamieszanie wynika z faktu, że mutable w lambda declarator dotyczy const -ness z operator(), a nie mutable -ility członków danych typu zamykającego . Byłoby bardziej naturalne używanie const, podobnie jak w funkcjach członkowskich, ale domyślam się, że komitet standardów chciał, aby domyślny był const.

+0

Cóż, zorientowałem się, co słowo '' zmienne 'ma dla lambda. Gdyby członkowie zamknięcia nie byli 'conststem ', potrzebowałbym' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' ''. Zastanawiam się tylko, czy istnieje sposób na skopiowanie zmiennych 'const' jako non -'const' do zamknięcia. – z33ky

+0

@ z33ky, przechwyć go niejawnie, skopiuj go jawnie :) '[&]() mutable {Foo f (foo); f.Func(); }; ' – chill

+0

Zwracam lambdę z funkcji, więc przechwycone odwołanie zostanie unieważnione. Chyba będę musiał sam skonstruować Funktora lub polegać na 'const_cast'. Lub żyj z jednym niepotrzebnym wywołaniem copy-ctor. – z33ky

0

Inna możliwość obejścia:

struct Foo 
    { 
     Foo(){} 
     void Func(){} 
    }; 

    int main() 
    { 
     const Foo foo; 
     { 
      Foo& fooo= const_cast<Foo&>(foo); 
      [fooo]() mutable { fooo.Func(); }; 
     } 
    } 

To rozwiązanie problemów bezpieczeństwa (przypadkowe zmiany const przedmiot przez const możliwe jest odniesienie), lecz dodatkowo kopiowanie jest unikać.

+0

Jeśli jest to konieczne, oznacza to, że 'Func()' powinno być oznaczone jako "const". Jeśli tak, to jest oddzielna kwestia od lambda, więc ta odpowiedź, choć jest prawdziwa, nie jest tak naprawdę rozwiązaniem. (Można albo naprawić 'Func()', aby uzyskać kwalifikację cv, albo jeśli modyfikacja nie jest możliwa, zapewnij wolną funkcję 'void Func_unsafe (const Foo & f) {const_cast (f) .Func();}' i użyj to w lambda, niezależnie od tego, lambda nie zajmuje się sprawą.) – GManNickG

+0

GManNickG: Jeśli Func naprawdę modyfikuje obiekt i jest oznaczony jako const, to jest to globalny problem bezpieczeństwa dla całego programu, za każdym razem, gdy pojawia się const Foo. Wprowadzając odwołanie do const-casted (zamiast tworzyć Func const), zlokalizowałem ten problem wewnątrz bloku wokół lambda, gdzie znajduje się referncja const-casted. Ponieważ foo używane jako const jest używane tylko do robienia kopii, jedynym ograniczeniem jest to, że ctor Ftor nie powinien modyfikować obiektu (jak zwykle). Niezupełnie, zgadzam się, ale jest to lepsze niż sprawienie, że Func() będzie na całym świecie. – user396672

+0

GManNickG: ... Aby unieważnić Func_unsafe(), odsłania UB, ponieważ modyfikuje obiekt, który jest rzeczywiście const (tj. Element stały klasy lambda) – user396672

Powiązane problemy