【问题标题】:Is it safe to capture a member reference if the class storing the original reference goes out of scope?如果存储原始引用的类超出范围,捕获成员引用是否安全?
【发布时间】:2017-12-01 11:33:03
【问题描述】:

考虑一下:

#include <iostream>
#include <functional>

std::function<void()> task;
int x = 42;

struct Foo
{
   int& x;

   void bar()
   {
      task = [=]() { std::cout << x << '\n'; };
   }
};

int main()
{
   {
      Foo f{x};
      f.bar();
   }

   task();
}

我的直觉是,由于在执行任务时实际的引用仍然存在,我们在遇到 lambda 时得到一个新绑定的引用,一切都很好。

但是,在我的 GCC 4.8.5 (CentOS 7) 上,我看到一些行为(在更复杂的程序中)表明这是 UB,因为 f 和引用 f.x 本身已经死亡.对吗?

【问题讨论】:

  • 我不确定我是否理解。在你绑定到int&amp; x 的任务中,当Foo 被销毁时它不再有效。
  • 我想程序的“字面意思”阅读表明我正在捕获一个超出范围的this,并且任务中的x 只是一个现在无效的缩写this-&gt;x.
  • @freakish:但我有吗?我没有将新变量绑定到::x 吗? r2 绑定到 int x = 42; int&amp; r1 = x; int&amp; r2 = r1; 中的是什么?
  • 在显式= 的事实中,对x 的令人讨厌的隐式捕获是真正的PITA。
  • @Bathsheba:我们有报酬吗? ;)

标签: c++ c++11 lambda


【解决方案1】:

要捕获成员引用,您需要使用以下语法(在 C++14 中引入):

struct Foo
{
   int & m_x;

   void bar()
   {
      task = [&l_x = this->m_x]() { std::cout << l_x << '\n'; };
   }
};

这样l_x 是一个int &amp; 存储在闭包中并引用相同的intm_x 引用并且不受Foo 超出范围的影响。

在 C++11 中,我们可以通过值捕获指针来解决此功能缺失的问题:

struct Foo
{
   int & m_x;

   void bar()
   {
      int * p_x = &m_x;
      task = [=]() { std::cout << *p_x << '\n'; };
   }
};

【讨论】:

  • 同意。仅从 C++14 开始可用,对吗?认为我将不得不在某处声明一个指针并按值获取。
  • 虽然我的问题是关于 C++11 的,但这已经足够接近了,因为它确认了问题并提出了我希望在未来使用的解决方案 - 并转换为 C++11 解决方案现在是微不足道的(并且有效,顺便说一句)。
【解决方案2】:

您可以通过创建引用的本地副本和显式捕获来捕获 C++11 中的引用成员以避免捕获this

void bar()
{
    decltype(x) rx = x; // Preserve reference-ness of x.
    static_assert(std::is_reference<decltype(rx)>::value, "rx must be a reference.");
    task = [&rx]() { std::cout << rx << ' ' << &rx << '\n'; }; // Only capture rx by reference.
}

【讨论】:

  • 这并没有遇到同样的问题,纯粹是因为我们已经消除了this 中介?
  • @LightnessRacesinOrbit 是的,因为它使用显式捕获 this 未捕获。如果成员不是引用,它会在编译时中断。
  • 我知道this 没有出现在您的示例中 - 我试图得出无可置疑的证据,证明这一更改会导致完全有效的程序
  • @LightnessRacesinOrbit 所有这些引用最终都会引用全局 x,因为如果我理解正确的话,使用引用初始化引用不会引用引用。
  • 是的,这是我认为我最初的解决方案会做的,但它没有,因此我谨慎 - 我想我希望你能用一个证明来充实它
猜你喜欢
  • 2018-07-21
  • 1970-01-01
  • 1970-01-01
  • 2015-11-02
  • 2016-07-11
  • 2016-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多