2012-12-04 12 views
9

Sto provando a catturare un oggetto const tramite copia in un lambda (mutabile). Il mio compilatore tuttavia lamenta che l'oggetto catturato è const.Oggetto const copiato in chiusura lambda non mutabile

Non dovrebbe essere possibile copiare l'oggetto come non-const?

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

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

compilazione con 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*’ 

compilazione con clang ++ 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(); }); 

Il documento standard (o meglio il progetto ...) definisce in 5.1.2.14 che "Il tipo [...] è il tipo dell'entità catturata corrispondente", quindi suppongo che includerebbe gli specificatori cv.
Tuttavia non sembra intuitivo.

+0

Si potrebbe copiare esplicitamente all'interno del proprio corpo lambda ma immagino che non sia quello che stai cercando. Ovviamente potresti prendere come riferimento il valore r come parametro dato che è C++ 11. – CashCow

+0

Qual è la domanda? – chill

+0

@chill Questa è la domanda: perché l'oggetto foo copiato all'interno di lambda è const? –

risposta

6

In primo luogo, il tipo di un'espressione lambda, che ha la cattura, è un tipo di classe (5.1.2 Espressioni lambda [expr.prim.lambda] # 3)

Quel tipo ha un operator(), che è di default const, a meno che mutable sia utilizzato nell'espressione lambda ([expr.prim.lambda] # 5)

Successivamente, per ogni entità acquisita come copia, un membro senza nome viene dichiarato nel tipo di chiusura. [expr.prim.lambda] # 14]

Se si crea in modo esplicito l'equivalente (per lo più) del tipo di acquisizione, tutto seguirà naturalmente dalla consueta semantica per le classi, i tipi qualificati e le funzioni membro qualificato const.

Esempio:

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(); }; 
} 

immagino la confusione deriva dal fatto che mutable nel lambda-dichiaratore riguarda la const -ness del operator(), non il mutable -ility dei membri di dati del tipo di chiusura . Sarebbe più naturale usare const, come con le funzioni dei membri, ma suppongo che il comitato degli standard abbia voluto che const fosse l'impostazione predefinita.

+0

Bene, ho capito cosa fa la parola chiave 'mutabile' per il lambda. Se i membri della chiusura non fossero '' '' '' '' '' '' ', allora avrei bisogno dello specificatore' mutable' sulla lambda per accedere alla funzione 'nonst'costa. Mi stavo chiedendo se esistesse un modo per copiare le variabili 'const' come non-' coerenti 'nella chiusura. – z33ky

+0

@ z33ky, catturalo implicitamente, copialo esplicitamente :) '[&]() mutabile {Foo f (foo); f.Func(); }; ' – chill

+0

Sto restituendo il lambda da una funzione, in modo tale che il riferimento acquisito venga invalidato. Credo che dovrò costruire un Funktor o fare affidamento su 'const_cast'. Oppure vivi con una sola chiamata copy-ctor non necessaria. – z33ky

0

Un'altra soluzione possibile:

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

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

Questa soluzione presenta problemi di sicurezza (modifica accidentale dell 'oggetto const facendo riferimento non const è possibile), ma la copia aggiuntiva è evitata.

+0

Se ciò è necessario, significa che 'Func()' avrebbe dovuto essere contrassegnato come 'const'. Se è così, questo è un problema separato dal lambda, quindi questa risposta, benché vera, non è in realtà una soluzione. (Si potrebbe correggere 'Func()' per essere qualificati cv, o se la modifica non è possibile fornire una funzione libera 'void Func_unsafe (const Foo & f) {const_cast (f) .Func();}' e usare a prescindere, il lambda non ha alcun problema con il problema.) – GManNickG

+0

GManNickG: Se Func modifica realmente l'oggetto ed è marcato const, è un problema di sicurezza globale per l'intero programma, ogni volta che compare const Foo. Introducendo il riferimento const-cast (invece di rendere Func const), ho localizzato questo problema all'interno del blocco attorno al lambda, dove risiede la refernce const-cast. Dato che il fooo a fusione costante è usato solo per fare una copia, l'unica restrizione è che il copyctor di Foo non dovrebbe modificare l'oggetto (come al solito). Non perfetto, sono d'accordo, ma è meglio che rendere Func() const globalmente. – user396672

+0

GManNickG: ... Come vuoto Func_unsafe(), espone UB poiché modifica un oggetto che è effettivamente const (cioè membro const della classe lambda) – user396672

Problemi correlati