2012-12-14 70 views
15

我已經從Objective-C背景轉向了C++ 11,並且我努力接受的一件事是C++ 11的不同捕獲語義lambdas與Objective-C「塊」。 (請參閱here進行比較)。異步使用C++ 11 lambda,安全

在Objective-C中,與C++類似,如果引用成員變量,則會隱式捕獲指針self/this。但由於在Objective-C中的所有對象都有效地「共享指針」,使用C++的術語,你可以這樣做:

doSomethingAsynchronously(^{ 
    someMember_ = 42; 
}); 

...和你保證其成員你所訪問的對象將在塊執行時保持活動狀態。你不必考慮它。在C++中的等效似乎是這樣的:

// I'm assuming here that `this` derives from std::enable_shared_from_this and 
// is already owned by some shared_ptr. 
auto strongThis = shared_from_this(); 

doSomethingAsynchronously([strongThis, this] { 
    someMember_ = 42; // safe, as the lambda holds a reference to this 
         // via shared_ptr. 
}); 

在這裏,你需要記住捕捉的shared_ptr除了這個指針。是否有一些不太容易出錯的方法來實現這一點?

+3

*「你不必考慮它。」*開始考慮它。它導致更好的設計。 – Pubby

+0

@Pubby但事情是,這是使用塊的輕鬆使用,使得它們在Obj-C世界中的一次性異步任務非常有用和普遍。如果他們擁有C++ 11語義,並且每次都必須問自己「這個對象是否還活着,這個對象是否還活着,這個對象是否還活着......」,我想很多人都會被誘惑說「擰它,我會同步做。」 –

+0

從'this'創建一個共享指針並不能保證它仍然存在,除非對象本身已經被共享指針所擁有並且您正在複製它。創建一個新的共享指針(通過'new shared_ptr (this)'或'make_shared (this)')只能用於獲得雙重刪除,除非內存會泄露。那麼,就你的情況而言,如果此時不創建共享指針,那麼'this'如何被刪除? – Agentlien

回答

6

C++的創始原則之一是,你不支付你不使用的東西。這意味着在這種情況下,不需要採用shared_ptrthis的上下文不應引起任何引用計數開銷。這也意味着它不應該自動發生,例如,作爲enable_shared_from_this的一個特性,因爲您可能希望將短暫的lambda傳遞給算法(for_each等),在這種情況下,lambda不會超出其範圍。

我建議適應lambda-wrapper pattern;在這種情況下它用於move捕獲一個大對象(How to capture std::unique_ptr "by move" for a lambda in std::for_each)的,但它同樣可以用於this共享捕獲:

template<typename T, typename F> 
class shared_this_lambda { 
    std::shared_ptr<T> t; // just for lifetime 
    F f; 
public: 
    shared_this_lambda(std::shared_ptr<T> t, F f): t(t), f(f) {} 
    template<class... Args> 
    auto operator()(Args &&...args) 
    -> decltype(this->f(std::forward<Args>(args)...)) { 
    return f(std::forward<Args>(args)...); 
    } 
}; 

template<typename T> 
struct enable_shared_this_lambda { 
    static_assert(std::is_base_of<std::enable_shared_from_this<T>, T>::value, 
    "T must inherit enable_shared_from_this<T>"); 
    template<typename F> 
    auto make_shared_this_lambda(F f) -> shared_this_lambda<T, F> { 
    return shared_this_lambda<T, F>(
     static_cast<T *>(this)->shared_from_this(), f); 
    } 
    template<typename F> 
    auto make_shared_this_lambda(F f) const -> shared_this_lambda<const T, F> { 
    return shared_this_lambda<const T, F>(
     static_cast<const T *>(this)->shared_from_this(), f); 
    } 
}; 

通過除了enable_shared_from_this繼承enable_shared_this_lambda使用;那麼你就可以明確要求,任何長期的lambda採取共享this

+0

是的,但如果涉及到線程,還需要someMember_原子或互斥體。 –

+0

謝謝,我想我會採用這種模式。仍然不完全相信這是一件好事,shared_ptr是一個圖書館級的功能,因爲它使得這個過程非常繁瑣,但我猜它們是休息時間。 –

2

其實,有一個正確的答案,這個問題。答案與shared_from_this()具有完全相同的效果(例如,當您使用boost::asio::io_service時)。想想看,與shared_from_this()有什麼約束力呢?它簡單地取代this。那麼是什麼阻止你完全用shared_from_this()代替this

按照你的榜樣,我更新,以賺取差價更清晰,而不是這樣的:

auto strongThis = shared_from_this(); 

doSomethingAsynchronously([strongThis, this]() { 
    this->someMember_ = 42; //here, you're using `this`... that's wrong! 
}); 

這樣做:

auto strongThis = shared_from_this(); 

doSomethingAsynchronously([strongThis]() //notice, you're not passing `this`! 
{ 
    strongThis->someMember_ = 42;    
}); 

這裏唯一的代價是你得來用strongThis->加上前綴。但這是最有意義的方式。