2012-12-04 11 views
9

Kopyalama yoluyla bir const nesnesini (değiştirilebilen) lambda içinde yakalamaya çalışıyorum. Ancak derleyicim, yakalanan nesnenin const olduğunu şikayet ediyor.Kopyalanamayan lambda kapamasında kopyalanmış const nesnesi

Nesneyi const olmayan olarak kopyalamak mümkün olmaz mı?

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

3.1 ++ clang derleme: g ++ 4.7.2 derleme

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

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

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

standart belgesi (ya da daha doğrusu taslak ...) 5.1.2.14 olarak tanımlar "[...] tipi, karşılık gelen yakalanmış varlığın türü" dir, bu yüzden cv-belirtecilerini içerecektir.
Ancak sezgisel görünmüyor.

+0

Açıkça lambda bedeninizin içine kopyalayabilirsiniz ama sanırım aradığınız şey bu değil. Tabii ki, C++ 11 verilen parametre olarak r-değeri referansı alabilirdiniz. – CashCow

+0

Soru nedir? – chill

+0

@chill Bu soru şudur: neden kopyalanmış foo nesnesi lambda içinde const nedir? –

cevap

6

İlk olarak, yakalama sahip bir lambda ifadesi, tip bir sınıf tipidir (5.1.2 Lambda ifadeleri [expr.prim.lambda] # 3)

Bu tür, varsayılan bir operator() sahip lambda ifadesinde kullanılan mutable sürece const ([expr.prim.lambda] # 5)

sonra, kopya olarak çekilen her bir taraf için, bir isimsiz elemanı kapak türü bildirilmiştir. [expr.prim.lambda] # 14]

Yakalama türünün (çoğunlukla) eşdeğerini açıkça oluşturuyorsanız, her şey, normal olarak sınıflar, yapıt nitelikli türler ve yapının kalifiye üye işlevleri için olağan anlambilimden gelecektir.

Örnek:

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

I karışıklık lambda Bildiricisi içinde mutableoperator() kapatma değil tip veri elemanlarının mutable -ility arasında const -lık ilgilidir olmasından kaynaklanmaktadır tahmin . Üye işlevlerinde olduğu gibi const'u kullanmak daha doğal olur, ancak standartlar komitesinin varsayılan olarak const olmasını isterdim.

+0

Eh, 'mutable' anahtar kelimenin lambda için ne yaptığını anladım. Eğer kapağın üyeleri, 'const'' değilse, o zaman' 'non''' işlevine erişmek için lambda' mutable'-specifier gerek duyardı. Ben sadece 'const' değişkenlerini kapatmaya 'const' olarak kopyalamanın bir yolu olup olmadığını merak ediyordum. – z33ky

+0

@ z33ky, örtülü olarak yakala, açıkça kopyala :) '[&]() mutable {Foo f (foo); f.Func(); }; '' – chill

+0

Lambda'yı bir işlevden döndürüyorum, böylece yakalanan başvuru geçersiz sayılır. Bir Funktor'u kendim inşa etmem ya da 'const_cast''e güvenmem gerekecek. Ya da gereksiz bir kopyalayıcı çağrı ile yaşa. – z33ky

0

Başka bir olası çözüm:

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

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

Bu çözelti güvenlik problemleri (const olmayan referans mümkündür yoluyla const nesnenin rastlantısal bir değişiklik), ancak ek kopyalama sahip önlenir.

+0

Eğer gerekli ise, 'Func()' 'const' olarak işaretlenmiş olmalıdır. Eğer öyleyse, bu lambdadan ayrı bir meseledir, bu yüzden bu cevap doğru iken gerçekten bir çözüm değildir. (Biri, Func() yi, cv niteliğine sahip olacak şekilde düzeltecek ya da değişiklik yapılamazsa, bir serbest işlev (void) Func_unsafe (const Foo & f) {const_cast (f) .Func();} 'yi kullanın ve kullanın Bu lambda, ne olursa olsun, lambda sorunu olan bir iş yoktur.) – GManNickG

+0

GManNickG: Eğer Func gerçekten nesneyi değiştirir ve const olarak işaretlenirse, Foo'nun ne zaman göründüğü tüm program için global güvenlik sorunudur. Const-casted referansını (Func const yapmak yerine) tanıtarak, bu konuyu, const-casted refernce'nin bulunduğu lambda etrafındaki bloğun içinde lokalize ettim. Const-casted fooo sadece bir kopya yapmak için kullanıldığı için, tek kısıtlama Foo'nun kopya cetveli nesneyi değiştirmemesidir (olağan durum nedir). Mükemmel değil, aynı fikirdeyim, ama Func() yapısını global olarak yapmaktan daha iyidir. – user396672

+0

GManNickG: ... Func_unsafe() 'i geçersiz kılmak için, gerçekte const olan bir nesneyi (yani, lambda sınıfının const üyesi) değiştirdiği için UB'yi gösterir. – user396672