2014-02-06 36 views
1

在C++中,而相比之下,語言,如C#,能夠指定是否與否封閉的範圍的變量應通過值或lambda表達式內的參考捕獲。這導致在其中可以未定義情況下通過與通過參考援引lambda表達式之前返回一個函數捕獲封閉範圍的λ:C++ 11:防止拉姆達範圍捕獲錯誤

void test() 
{ 
    int t = 1; 
    enqueue_task([&]() { do_something(t); }); 
} 

在這種情況下,「T」,將最有可能是當由lambda表達式指定的任務計劃執行時超出範圍。顯然,這會導致醜陋的錯誤。

我的解決方案將是一個語言的功能是這樣的:

template<class T> 
void enqueue_task(T lambda) 
{ 
    static_assert(!std::is_lambda<T>::value || std::is_lambda_captured_by_value<T>::value, 
     "The lambda expression is executed asynchronously and therefore capturing eclosing state via reference is forbidden."); 

    // enqueue task for execution 
} 

對我來說,這將是一個乾淨的「非侵入式」延伸,將允許中間件的作家,以保護他們的API被濫用。當然,它不提供防彈保護,因爲我仍然可以通過價值傳遞一個指向棧對象的指針,可能更多。無論如何,當通過值傳遞時仍然默默地導致未定義行爲的代碼本身可能已經是可疑的。

有沒有類似的東西我可以做,已經支持?

對我來說,目前一個理智的解決方案似乎在延期執行情況下不允許任何lambda表達式。例如,一個事件處理程序不應該被允許爲lambda類型。說起來容易做起來難,因爲這也意味着我不能使用std :: function並且必須返回到舊的函數類型。

一個更好的辦法是引入關鍵字的種類,如:

void test() 
{ 
    int t = 1; 
    enqueue_task(deferred() { do_something(t); }); 
} 

這將確保,所有指編譯器可以,通過lambda函數將適用於延遲執行,這意味着它的封閉範圍消失了。

我認爲C++ 11已經很長的路要讓C++編程變得安全。這個拉姆達的東西是少數幾個你仍然用槍指着你的腳的地方之一。它只是一個滴答作響的時間炸彈。

+0

「目前一個理智的解決方案似乎在延期執行情況下不允許任何lambda表達式。」那麼你失去了形成關閉的能力。 – JAB

+0

@JAB:Ofc,但我並不是說不允許標準方式,而是API方式。因此,如果lambdas不會影響您的API的可用性,並且有可能會忘記您的API調用lambda延期,那麼您應該不使用lambdas來執行此API。 API應該強制正確使用。 – thesaint

+0

確實如此,但它不像lambda表達式是唯一可以解決您擔心的問題的方法。如果用戶傳遞一個非lambda函數會發生一些涉及超出範圍引用的混亂情況,會發生什麼情況?或者,上帝保佑,生指針?真正強制正確使用API​​的唯一方法是阻止用戶提供任何類型的輸入(即使沒有這樣做,如果您不小心如何設置約束,最終可能會產生誤報,其中有效參數被拒絕,因爲它們沒有按照您的要求設置)。 – JAB

回答

3

通常的補救措施是通過值[=]() {...}進行捕獲。
當複製實際的對象是不可行的,它通常是有益通過shared_ptr,這可能會提供更便宜的複製,這取決於上下文中使用它,也可以讓你分享所有權這樣,無論是主叫方和延期lambda獨立使用它。

C++ 14應具有移動捕獲語義,這將解決複製對象,則不需要共享時的性能問題。

否則,通過-Ref是你想要的。像C++中的所有內容一樣,不僅僅在lambda表達式中,當你開始傳遞指針和引用時,你需要小心。

+0

好的,儘管JAB早些時候說過這件事,但只是作爲一個評論:P。 – thesaint

+0

@thesaint注意他說*一些*。這裏IMO的重要部分是按值捕獲;以及您需要相同對象的位置,請按值使用shared_ptrs。由於原子操作,shared_ptr在某些場景下複製仍然有點昂貴,這就是爲什麼我提到即將採用移動捕捉語義即將推出的原因。 –