我知道这可能相当令人困惑,但我正在使用 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/