2012-12-14 45 views
15

Tôi đã đến C++ 11 từ một nền Objective-C, và một điều tôi đang gặp khó khăn để đến với các điều khoản khác nhau là ngữ nghĩa bắt giữ của C++ 11 lambdas vs Objective-C "khối". (Xem here để so sánh).Sử dụng C++ 11 lambdas không đồng bộ, an toàn

Trong mục tiêu-C, như C++, con trỏ self/this bị ẩn hoàn toàn nếu bạn tham chiếu đến biến thành viên. Nhưng bởi vì tất cả các đối tượng trong Objective-C là một cách hiệu quả "con trỏ được chia sẻ", sử dụng thuật ngữ C++, bạn có thể làm điều này:

doSomethingAsynchronously(^{ 
    someMember_ = 42; 
}); 

... và bạn chắc chắn rằng đối tượng có thành viên mà bạn đang truy cập sẽ còn sống khi khối thực hiện. Bạn không cần phải suy nghĩ về nó. Tương đương trong C++ có vẻ giống như:

// 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. 
}); 

Ở đây, bạn cần phải nhớ chụp shared_ptr ngoài con trỏ này. Có một số cách ít dễ bị lỗi hơn để đạt được điều này?

+3

* "Bạn không cần phải suy nghĩ về nó." * Bắt đầu suy nghĩ về nó. Nó dẫn đến thiết kế tốt hơn. – Pubby

+0

@Pubby Nhưng vấn đề là, đó là nỗ lực của việc sử dụng các khối làm cho chúng trở nên hữu ích và phổ biến cho các tác vụ không đồng bộ một-shot trong thế giới Obj-C. Nếu họ có ngữ nghĩa C++ 11, và bạn phải tự hỏi mình "liệu vật thể này có còn sống không, vật thể này sẽ còn sống, vật thể này sẽ còn sống ..." mỗi lần, tôi nghĩ nhiều người sẽ bị cám dỗ nói "vít nó, tôi sẽ làm nó một cách đồng bộ." –

+0

Tạo một con trỏ được chia sẻ từ 'this' không đảm bảo nó sẽ vẫn tồn tại, trừ khi chính đối tượng đã được sở hữu bởi một con trỏ được chia sẻ và bạn đang sao chép nó. Tạo một con trỏ được chia sẻ mới (bởi 'shared_ptr mới (this)' hoặc 'make_shared (this)') sẽ chỉ phục vụ để có được một double-delete, trừ khi bộ nhớ sẽ bị rò rỉ nếu không. Vì vậy, trong trường hợp của bạn, làm thế nào để 'này' bị xóa nếu bạn không tạo một con trỏ chia sẻ tại thời điểm này? – Agentlien

Trả lời

6

Một trong những nguyên tắc sáng lập của C++ là bạn không trả tiền cho những gì bạn không sử dụng. Điều đó có nghĩa là trong trường hợp này, các ngữ cảnh khi lấy shared_ptr đến this là không cần thiết, không nên chịu bất kỳ chi phí đếm tham chiếu nào. Điều này cũng có nghĩa là nó không nên xảy ra tự động ngay cả ví dụ: như một tính năng của enable_shared_from_this, vì bạn có thể muốn truyền một lambda ngắn ngủi đến một thuật toán (for_each, v.v.) trong trường hợp đó lambda không vượt quá phạm vi của nó.

Tôi khuyên bạn nên điều chỉnh lambda-wrapper pattern; trong trường hợp đó nó được sử dụng cho move bắt giữ một đối tượng lớn (How to capture std::unique_ptr "by move" for a lambda in std::for_each), nhưng nó không kém có thể được sử dụng cho chụp chung của 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); 
    } 
}; 

Sử dụng bằng cách kế thừa enable_shared_this_lambda ngoài enable_shared_from_this; bạn có thể sau đó một cách rõ ràng yêu cầu rằng bất kỳ lambdas tồn tại lâu dài hãy chia sẻ this:

+0

Có, nhưng bạn cũng cần phải có một sốMember_ nguyên tử hoặc mutexed nếu chủ đề có liên quan. –

+0

Cảm ơn, tôi nghĩ tôi sẽ áp dụng mẫu này. Vẫn không hoàn toàn tin rằng đó là một điều tốt mà shared_ptr là một tính năng cấp thư viện cho rằng nó làm cho điều này rất cồng kềnh, nhưng chúng là sự phá vỡ, tôi đoán vậy. –

2

Trên thực tế, có một câu trả lời đúng cho vấn đề này . Câu trả lời có cùng hiệu quả ràng buộc với shared_from_this() (giống như khi bạn làm điều đó với boost::asio::io_service). Hãy suy nghĩ về nó; những gì ràng buộc với shared_from_this() làm gì? Nó đơn giản thay thế this. Vì vậy, những gì ngăn cản bạn thay thế this với shared_from_this() hoàn toàn?

Tiếp theo ví dụ của bạn, mà tôi cập nhật để tạo sự khác biệt rõ ràng hơn, thay vì điều này:

auto strongThis = shared_from_this(); 

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

Làm điều này:

auto strongThis = shared_from_this(); 

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

Chi phí duy nhất ở đây là bạn đang gonna phải tiền tố mọi thứ với strongThis->. Nhưng đây là cách có ý nghĩa nhất để làm điều đó.

Các vấn đề liên quan