在C ++中,在lambda中捕获“ * this”

时间:2018-11-07 03:31:43

标签: c++ c++11 lambda c++14 c++17

sudo chmod u=rwx,g=rx,o=rx libcudnn.so.6.5.18
sudo ln -s libcudnn.so.6.5.18 libcudnn.so.6
sudo ln -s libcudnn.so.6 libcudnn.so

为什么lambda struct S { int x = 4; void f(int i); }; void S::f(int i) { [=, *this]{}; // OK: captures this by value. See below. } 有效?

1 个答案:

答案 0 :(得分:1)

不确定您的意思,但是p0018r0可能会回答您的问题:

  

2按值λ捕获* this的动机

     

通过按值真正捕获*,可以在调用闭包的功能之前复制隐式声明的闭包。

     

2.1 lambda的异步分发

     

闭包的异步分发是并行性和并发性的基石。   当从非静态成员函数中通过std :: async或其他并发/并行性调度机制异步调度lambda时,不能通过值捕获封闭的*此类。因此,当派发的lambda的未来(或其他句柄)超过原始类时,该lambda捕获的此指针无效。

class Work {
private:
  int value ;
public:
  Work() : value(42) {}
  std::future<int> spawn()
    { return std::async( [=]()->int{ return value ; }); }
};

std::future<int> foo()
{
  Work tmp ;
  return tmp.spawn();
  // The closure associated with the returned future
  // has an implicit this pointer that is invalid.
}

int main()
{
  std::future<int> f = foo();
  f.wait();
  // The following fails due to the
  // originating class having been destroyed
  assert( 42 == f.get() );
  return 0 ;
}
     

2.2向数据分发异步闭包

     

当前和将来专门针对并行性和并发性的硬件体系结构具有异构存储系统。例如,NUMA区域,附加的加速器存储器和内存处理(PIM)堆栈。在这些体系结构中,如果将闭包复制到其操作所在的数据上,则通常会显着提高性能,而不是将数据移入和移出闭包。

     

例如,如果驻留在同一NUMA区域中的该闭包的副本对该数据起作用,则在跨NUMA区域的大数据上并行执行闭包将具有更高的性能。如果为并行调度(例如在并行技术规范中)提供了一个(自包含)按值捕获的lambda闭包,则该库可以在每个NUMA区域内创建该闭包的副本,以改善并行数据的局部性计算。再举一个例子,在执行发生之前,必须将分配给具有单独内存的附加加速器的闭包复制到加速器的内存中。因此,当前和将来的体系结构都需要具有将闭包复制到数据的功能。

     

2.3繁琐且容易出错的解决方法

     

对此缺陷的一种可能的解决方法是显式捕获原始类的副本。

class Work {
private:
  int value ;
public:
  Work() : value(42) {}
  std::future<int> spawn()
    {
      return std::async( [=,tmp=*this]()->int{ return tmp.value ; });
    }
};
     

此替代方法有两个责任。首先,还捕获了this指针,这提供了一个重要的机会来错误地引用this->成员而不是tmp。会员。其次,在现有代码中引入异步分派的lambda表达式是繁重且适得其反的。像在并行技术规范中那样,考虑用每个结构的并行结构在非静态成员函数中替换for循环的情况。

class Work {
public:
  void do_something() const {
    // for ( int i = 0 ; i < N ; ++i )
    foreach( Parallel , 0 , N , [=,tmp=*this]( int i )
    {
      // A modestly long loop body where
      // every reference to a member must be modified
      // for qualification with 'tmp.'
      // Any mistaken omissions will silently fail
      // as reference