c++ - lambda 捕获变量中的垃圾值作为回调

标签 c++ lambda

我知道这可能相当令人困惑,但我正在使用 Boost 测试框架编写单元测试。我试图简单地增加一个变量来测试特定回调是否按预期执行。

这是测试代码摘录:

  uint32_t nSuccessCallbacks = 0;
  uint32_t nFailureCallbacks = 0;
  auto successCallback = [&nSuccessCallbacks, this] {
    std::cout << "Running success callback...\n";
    ++nSuccessCallbacks;
  };

  auto failureCallback = [&nFailureCallbacks, this] (const std::string& str) {
    std::cout << "Error code: " << str << "\n";
    std::cout << "Running failure callback...\n";
    ++nFailureCallbacks;
  };

  dest.advertise(rr, successCallback, failureCallback);

advertise的定义:

void
NfdRibReadvertiseDestination::advertise(nfd::rib::ReadvertisedRoute& rr,
                                        std::function<void()> successCb,
                                        std::function<void(const std::string&)> failureCb)
{
  m_controller.start<ndn::nfd::RibRegisterCommand>(
    ControlParameters().setName(rr.getPrefix()).setOrigin(ndn::nfd::ROUTE_ORIGIN_CLIENT).setFlags(ndn::nfd::ROUTE_FLAG_CHILD_INHERIT),
    [&] (const ControlParameters& cp) { successCb(); },
    [&] (const ControlResponse& cr) { failureCb(cr.getText()); });
}

仅供引用,dest 在测试夹具中定义。

我无法修改 nSuccessCallbacks。每当调用回调时,我们都会正确地单步执行它,但是在回调退出并且我们在 dest.advertise() 之后的代码中,值仍然是 0。我们成功地到达了回调 lambda,但是gdb 报告范围内没有这样的变量。我已经尝试了所有捕获、特定捕获、混合在 this 中并删除它等的所有合理组合。我不知道为什么捕获子句错误地捕获了变量。我最好的猜测是,由于 lambda 被传递到另一个 lambda,第一个 lambda 的捕获子句丢失了吗?

编辑:回调在接口(interface)对象接收数据时执行。我们在稍后的测试中对此进行了模拟,因为它不重要,所以我选择不包含它。

最佳答案

使用 Crystal 球,您的 lambda 将在您通过引用捕获某些内容(广告 或您的“测试代码摘录”)的众多范围之一退出后运行。因此,通过引用捕获的变量已经离开作用域和 UB 结果,您会看到垃圾。

您发布的代码实际上并没有运行 lambda,因此很明显,发布的代码对于包含垃圾的 lambda 没有这样的问题。

作为一般规则,如果您的 lambda 或其任何拷贝可能超过当前范围,则永远不要通过引用捕获。通过复制或(在 C++14 中)通过移动捕获。此规则也有异常(exception),但它们很容易成为错误的来源。

作为第二条规则,如果您的 lambda 超出当前范围,请明确捕获您捕获的所有内容。没有默认捕获。这样你就不会对生命周期(或指向的生命周期)不够长的东西感到惊讶,比如 this 或一些指针或类似的东西。

至少要这样做:

[successCb] (const ControlParameters& cp) { successCb(); },
[failureCb] (const ControlResponse& cr) { failureCb(cr.getText()); }

然后确保这个,并且没有这个的拷贝:

auto successCallback = [&nSuccessCallbacks, this] {
  std::cout << "Running success callback...\n";
  ++nSuccessCallbacks;
};

不会超出其范围。如果是,请更改您的捕获方式。

关于c++ - lambda 捕获变量中的垃圾值作为回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40853049/

相关文章:

python - C++ 与 Python 嵌入 : crash if Python not installed

C++ 将 Const 对象引用传递给构造函数

java - 查找多个列表的最大值

c# - Entity Framework 同时根据2列排序

xcode - 在 Xcode 中,如何进入 lambda 函数?

c++ - 我可以在头文件中#define QStringLiterals 吗?

c++ - 在 C++ 中表示时间

c++ - 初始化和删除二维数组

java - 如何使用 lambda 表达式调试stream().map(...)?

performance - 在 LINQ 中加入查询语法或方法语法应该使用什么?