2012-12-04 25 views
9

我試圖通過(可變)lambda中的副本捕獲一個const對象。不過,我的編譯器抱怨說,捕獲的對象是const。在lambda封閉中複製常量對象不可改變

不應該可以將對象作爲非常量來複制嗎?

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

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

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

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

標準文件(或者更確切地說,草案...)定義5.1.2.14 「該類型是相應的被捕獲實體的類型」,所以我想這將包括cv-specifiers。
雖然它並不直觀。

+0

你可以明確地拷貝你的lambda體內部,但我想這不是你想要的。您當然可以將r值參考作爲給定C++ 11的參數。 – CashCow

+0

是什麼問題? – chill

+0

@chill這是一個問題:爲什麼在lambda中複製的foo對象是const? –

回答

6

首先,lambda表達式,其具有捕獲的類型,是一個類類型(5.1.2 Lambda表達式[expr.prim.lambda]#3)

這種類型的具有operator()這是默認const,除非在拉姆達表達式中使用mutable([expr.prim.Lambda]#5)

接下來,對於捕獲爲副本的每個實體,在封閉類型中聲明未命名的成員。 [expr.prim.lambda]#14]

如果您明確地構建捕獲類型的(大部分)等價物,那麼一切自然會遵循類,const限定類型和const限定成員函數的通常語義。

實施例:

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

我想的混亂從mutable在λ-說明符涉及const -ness的operator(),而不是閉合類型的數據成員的mutable -ility的事實莖。與成員函數一樣,使用const會更自然,但我想標準委員會希望const是默認值。

+0

那麼,我想出了lambda的'mutable'關鍵字。如果閉包的成員是非''constst',那麼我需要lambda上的'mutable'說明符來訪問非''constst'函數。我只是想知道是否有方法將'const'變量作爲非''constst''複製到閉包中。 – z33ky

+0

@ z33ky,隱式捕獲,明確複製:)'[&]()mutable {Foo f(foo); f.Func(); };' – chill

+0

我從函數返回lambda表達式,以便捕獲的引用無效。我想我必須自己構建一個Funktor或依靠'const_cast'。或與一個不必要的copy-ctor通話。 – z33ky

0

另一個可能的解決方法:

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

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

這種解決方案具有安全問題(通過非const引用可以const對象的意外修改),但附加的複製得以避免。

+0

如果這是必要的,這意味着'Func()'應該被標記爲'const'。如果是這樣,這是一個與lambda分開的問題,所以這個答案雖然是真的,但並不是真正的解決方案。 (或者將'Func()'固定爲cv-qualified,或者如果修改不可行,則提供一個自由函數void Func_unsafe(const Foo&f){const_cast (f).Func();}'並且使用在lambda中;無論lambda與這個問題沒有任何關係)。 – GManNickG

+0

GManNickG:如果Func確實修改了對象並標記爲const,那麼對於整個程序來說,只要const Foo出現,它就是全局安全問題。通過引入常量鑄造引用(而不是製作Func常量),我將此問題定位在常量鑄造引用所在的lambda塊周圍。由於常量化fooo僅用於製作副本,唯一的限制是Foo的副本不應該修改該對象(通常情況如何)。不完美,我同意,但它比全局使Func()const更好。 – user396672

+0

GManNickG:...至於void Func_unsafe(),它暴露了UB,因爲它修改了一個實際上是const的對象(即lambda類的const成員) – user396672