是否可以将自身内部的一个std::function
替换为另一个std::function
?
以下代码无法编译:
#include <iostream>
#include <functional>
int main()
{
std::function<void()> func = []()
{
std::cout << "an";
*this = std::move([]() { std::cout << "bn"; });
};
func();
func();
func();
}
可以修改编译吗?
现在的错误消息是:">this"没有被捕获到这个lambda函数- 我完全理解。然而,我不知道我如何捕捉到func
的this
指针。我猜,它甚至不是λ内部的std::function
,还?!如何做到这一点?
背景:我想要实现的是:在第一次调用给定std::function
时,我想做一些初始化工作,然后用优化的函数替换原始函数。我想为我的函数的用户透明地实现这一点。
上面示例的预期输出为:
阿
b b
您不能在 lambda 中使用this
来引用 lambda。this
将仅引用封闭类,在您的情况下没有封闭类,因此您不能使用this
.但是,您可以做的是捕获func
并重新分配:
std::function<void()> func = [&func]()
{
std::cout << "an";
func = []() { std::cout << "bn"; }; // note the missing move, a lambda
// is already an rvalue
};
但是请注意,如果您让func
超出其范围(例如通过按值从函数返回它(而不先调用它(有效地重新分配存储的函数对象(,那么您将得到一个悬而未决的引用。
我猜,它甚至不是λ内部的
std::function
,还?!
其实是。名称紧跟在其声明符之后进入范围,因此在=
之前,引入了类型std::function<void()>
的func
。因此,在您引入 lambda 的那一刻,您已经可以捕获func
.
这在技术上解决了您的问题:
std::function<void()> func = [&func]{
std::cout << "an";
func = []{ std::cout << "bn"; };
};
但这不是一个好的计划。 std 函数的生存期和行为与局部堆栈变量相关联。 副本几乎在任何意义上都无法做你想要的 - 它们继续打印"ab"
除非它们因为原始func
超出范围而出现段错误。
为了解决这个问题,我们必须部署一些大枪;首先,函数式编程女王Y Combinator女士:
std::function<void()> func = y_combinate( [](auto&& self){
std::cout << "an";
self = []{ std::cout << "b"; };
} );
Y 组合器获取签名F = (F,Args...)->R
的函数,然后返回签名(Args...)->R
的函数。 这是无状态语言管理递归的方式,当你在被赋予名字之前无法命名自己时。
编写 Y 组合器比你在C++中担心的要容易:
template<class F>
struct y_combinator_t {
F f;
template<class...Args>
auto operator()(Args&&...args)
-> typename std::result_of< F&( F&, Args&&... ) >::type
{
return f( f, std::forward<Args>(args)... );
}
};
template<class F>
y_combinator_t<typename std::decay<F>::type> y_combinate( F&& f ) {
return {std::forward<F>(f)};
}
遗憾的是,这不起作用,因为传递给 lambda 的self
类型实际上是原始 lambda 的类型。 而b打印λ是一种不相关的类型。 因此,当您尝试self = []{ std::cout << "bn"; }
时,您会在一些相对较深的模板垃圾邮件错误中嵌入错误。
悲伤;但只是暂时的挫折。
我们需要的是一个非常难以命名的类型 - 一个F = std::function<void(F)>
- 一个将相同类型的对象的实例作为其一个参数的std::function
。
通常没有办法做到这一点,但需要一些模板愚蠢......在这里,我以前做过。
然后你的代码显示:
std::function<void()> func = y_combinate( recursive_func< void(own_type&) >([](auto&& self){
std::cout << "an";
self = [](auto&&){ std::cout << "bn"; };
}) );
对给定副本的调用func
首先打印"an"
,然后每个后续调用打印"bn"
。 b打印机的副本也会打印b,但a打印机的副本将在转换前第一次打印a。
活生生的例子。
此代码中的self
是一个recursive_func< void(own_type&) >
,因此您可以在 B 打印机中执行与在 A 打印机中相同的操作。