17

我从 Objective-C 背景来到 C++11,我正在努力接受的一件事是 C++11 lambda 与 Objective-C“块”的不同捕获语义。(请参阅此处进行比较)。

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

在这里,除了 this 指针之外,您还需要记住捕获 shared_ptr。是否有一些不易出错的方法来实现这一目标?

4

3 回答 3

8

C++ 的基本原则之一是不用为不用的东西付费。这意味着在这种情况下,不需要使用shared_ptrtothis的上下文不应该产生任何引用计数开销。这也意味着它不应该自动发生,例如作为 的一个特性enable_shared_from_this,因为您可能希望将一个短暂的 lambda 传递给算法(for_each等),在这种情况下 lambda 不会超过其范围。

我建议调整lambda-wrapper 模式;在这种情况下,它用于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_this_lambda除了enable_shared_from_this;通过继承使用 然后,您可以显式请求任何长期存在的 lambdas 采用 shared this

doSomethingAsynchronously(make_shared_this_lambda([this] {
  someMember_ = 42;
}));
于 2012-12-14T13:01:34.363 回答
6

升压用途:

auto self(shared_from_this());
auto l = [this, self] { do(); };

这里提到:在 lambda 函数中使用 auto self(shared_from_this()) 变量的原因是什么?

于 2016-02-17T19:50:51.517 回答
2

实际上,这个问题有一个正确的答案。答案与绑定具有完全相同的效果shared_from_this()(就像您使用 绑定时一样boost::asio::io_service)。想想看;绑定有shared_from_this()什么作用?它简单地替换this. 那么是什么阻止你完全this替换呢?shared_from_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->. 但这是最有意义的方式。

于 2017-05-05T18:19:23.343 回答