我正在同时学习boost :: asio和C ++ 11。我的一个测试程序实际上是one of the samples given in the boost::asio tutorial的改编版,如下所示:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
class printer {
// Static data members
private:
const static boost::posix_time::seconds one_second;
// Instance data members
private:
boost::asio::deadline_timer timer;
int count;
// Public members
public:
printer(boost::asio::io_service& io)
: timer(io, one_second), count(0) {
std::function<void(const boost::system::error_code&)> callback;
callback = [&](const boost::system::error_code&) { // critical line
if (count < 5) {
std::cout << "Current count is " << count++ << std::endl;
timer.expires_at(timer.expires_at() + one_second);
timer.async_wait(callback);
}
};
timer.async_wait(callback);
}
~printer() {
std::cout << "Final count is " << count << std::endl;
}
};
const boost::posix_time::seconds printer::one_second(1);
int main() {
boost::asio::io_service io;
printer p(io);
io.run();
return 0;
}
当我运行此程序时,我遇到了分段错误。我明白为什么会出现分段错误。构造函数完成运行后,构造函数的callback
变量超出范围,lambda的callback
变量(它是对构造函数的callback
变量的引用)变为悬空引用。 / p>
所以我用以下方法修改关键线:
callback = [callback, &](const boost::system::error_code&) { // critical line
然后编译它,运行它,并得到一个错误的函数调用错误。再次,我明白为什么我得到了错误的函数调用错误。在lambda的范围内,构造函数的callback
变量仍然没有被赋值给任何值,所以它实际上是一个悬空函数指针。因此,lambda的callback
变量是构造函数的callback
变量的副本,也是一个悬空函数指针。
在考虑了这个问题一段时间后,我意识到我真正需要的是回调能够使用函数指针引用自身,而不是对函数指针的引用。该示例通过使用命名函数作为回调而不是匿名函数来实现此目的。但是,将命名函数作为回调传递并不是很优雅。有没有办法让匿名函数有一个函数指针将自己作为局部变量?
答案 0 :(得分:8)
有几种选择:
让lambda存储一个智能指针,指向存储lambda的动态分配的std::function
。例如:
auto pCallback = std::make_shared<std::function<void(const boost::system::error_code&)>>();
auto callback = [=](const boost::system::error_code&) { // critical line
if (count < 5) {
std::cout << "Current count is " << count++ << std::endl;
timer.expires_at(timer.expires_at() + one_second);
timer.async_wait(pCallback.get());
}
};
*pCallback = callback;
答案 1 :(得分:3)
要了解Asio和C ++ 11,我建议由asio本人的设计者使用boostcon“为什么C ++ 0x是网络编程最棒的语言”。 (Christopher Kohlhoff)
https://blip.tv/boostcon/why-c-0x-is-the-awesomest-language-for-network-programming-5368225 http://github.com/chriskohlhoff/awesome
在本次演讲中,C.K采用典型的小型asio应用程序,并开始逐个添加C ++ 11功能。谈话的中间部分有关于lambda的部分内容。您使用lambda的生命周期的问题是使用以下模式解决方法:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <memory>
class printer
{
// Static data members
private:
const static boost::posix_time::seconds one_second;
// Instance data members
private:
boost::asio::deadline_timer timer;
int count;
// Public members
public:
printer(boost::asio::io_service& io)
: timer(io, one_second), count(0) {
wait();
}
void wait() {
timer.async_wait(
[&](const boost::system::error_code& ec) {
if (!ec && count < 5) {
std::cout << "Current count is " << count++ << std::endl;
timer.expires_at(timer.expires_at() + one_second);
wait();
}
});
}
~printer() {
std::cout << "Final count is " << count << std::endl;
}
};
const boost::posix_time::seconds printer::one_second(1);
int main() {
boost::asio::io_service io;
printer p(io);
io.run();
return 0;
}
答案 2 :(得分:2)
现在有人向add a Y-combinator to the C++ standard library (P0200R0)提出解决此问题的建议。
这里的基本思想是将lambda作为第一个参数传递给自己。一个不可见的助手类通过将lambda存储在一个命名成员中来处理后台的递归调用。
提案中的示例实现如下所示:
#include <functional>
#include <utility>
namespace std {
template<class Fun>
class y_combinator_result {
Fun fun_;
public:
template<class T>
explicit y_combinator_result(T &&fun): fun_(std::forward<T>(fun)) {}
template<class ...Args>
decltype(auto) operator()(Args &&...args) {
return fun_(std::ref(*this), std::forward<Args>(args)...);
}
};
template<class Fun>
decltype(auto) y_combinator(Fun &&fun) {
return y_combinator_result<std::decay_t<Fun>>(std::forward<Fun>(fun));
}
} // namespace std
可以使用以下方法解决问题:
timer.async_wait(std::y_combinator([](auto self, const boost::system::error_code&) {
if (count < 5) {
std::cout << "Current count is " << count++ << std::endl;
timer.expires_at(timer.expires_at() + one_second);
timer.async_wait(self);
}
}));
注意传递给lambda的self
参数。
这将绑定到y_combinator
调用的结果,该调用是一个函数对象,它等同于已绑定self
参数的lambda(即,其签名为void(const boost::system::error_code&)
)。
答案 3 :(得分:1)
当lambda运行无效时,lambda将通过引用局部变量“callback”来捕获。
答案 4 :(得分:1)
只有理论:你可以用所谓的组合器(比如I,S,K)来做这些事情。
在使用F类型的匿名lambda表达式e
之前,您可以先定义 doubleF 等函数; (F) - &gt; (F,F)或 applyToOneself :( F f) - &gt; F = {return f(f); }。