我想弄清楚如何获取lambda函数自身的地址。下面是一个示例代码:
[]() { std::cout << "Address of this lambda function is => " << ???? }();
我知道我可以在变量中捕获lambda并打印地址,但是我想在执行这个匿名函数的时候就这样做。有没有更简单的方法?
bxpogfeg1#
在c++23之前的lambda中,无法直接获取lambda对象的地址。现在,碰巧的是,这是非常有用的,最常见的用法是为了递归。y_combinator来自于那些在定义之前你不能谈论自己的语言,它可以很容易地在c++中实现:
y_combinator
template<class F> struct y_combinator { F f; template<class...Args> decltype(auto) operator()(Args&&...args) const { return f( f, std::forward<Args>(args)... ); } template<class...Args> decltype(auto) operator()(Args&&...args) { return f( f, std::forward<Args>(args)... ); } }; template<class F> y_combinator(F)->y_combinator<F>;
现在你可以这样做:
y_combinator{ [](auto& self)-> void { std::cout<<"Address of this lambda function is => "<< &self; } }();
有几个有用的变体。我发现特别有用的一个变体是:
template<class F> struct y_combinator { F f; template<class...Args> decltype(auto) operator()(Args&&...args) const { return f( *this, std::forward<Args>(args)... ); } template<class...Args> decltype(auto) operator()(Args&&...args) { return f( *this, std::forward<Args>(args)... ); } };
其中,可以调用传递的self,而无需将self作为第一个参数传递。我相信第二个匹配真实的的y组合子(也就是不动点组合子),你想要哪个取决于你所说的“lambda地址”。还有这样一句精辟的话:
self
template<class R, class...Args> auto Y = [] (auto f) { auto action = [=] (auto action) -> std::function<R(Args...)> { return [=] (Args&&... args)->R { return f( action(action), std::forward<Args>(args)... ); }; }; return action(action); };
其返回STD函数。在c++23中,在lambda中访问this变得更加容易:
this
auto fib = [](this auto& self, int n) { if (n < 2) return n; return self(n-1) + self(n-2); };
你可以把第一个参数标记为this,它就变成了它自己,它甚至可以使用重载集合技巧,其中self是最容易推导的类型。
ubby3x7f2#
这不是直接可能的。然而,lambda捕获是类,对象的地址与它的第一个成员的地址一致。因此,如果你按值捕获一个对象作为第一个捕获,第一个捕获的地址对应于lambda对象的地址:
int main() { int i = 0; auto f = [i]() { printf("%p\n", &i); }; f(); printf("%p\n", &f); }
输出:
0x7ffe8b80d820 0x7ffe8b80d820
或者,您可以创建一个decorator design pattern lambda,将对lambda捕获的引用传递到其调用操作符中:
template<class F> auto decorate(F f) { return [f](auto&&... args) mutable { f(f, std::forward<decltype(args)>(args)...); }; } int main() { auto f = decorate([](auto& that) { printf("%p\n", &that); }); f(); }
wz3gfoph3#
解决这个问题的一个方法是用一个手写的函子类来替换lambda,这也是lambda的本质。然后,你可以通过this得到地址,甚至不用把函子赋给变量:
#include <iostream> class Functor { public: void operator()() { std::cout << "Address of this functor is => " << this; } }; int main() { Functor()(); return 0; }
Address of this functor is => 0x7ffd4cd3a4df
这样做的优点是100%可移植,并且非常容易推理和理解。
hwamh0ep4#
捕获lambda:
std::function<void ()> fn = [&fn]() { std::cout << "My lambda is " << &fn << std::endl; }
kyvafyod5#
这是可能的,但高度依赖于平台和编译器优化。在我所知道的大多数架构中,都有一个寄存器叫做指令指针,这个解决方案的要点是当我们在函数内部时提取它。在amd64上,下面的代码应该会给予你一个接近函数的地址。
#include <iostream> void* foo() { void* n; asm volatile("lea 0(%%rip), %%rax" : "=a" (n)); return n; } auto boo = [](){ void* n; asm volatile("lea 0(%%rip), %%rax" : "=a" (n)); return n; }; int main() { std::cout<<"foo"<<'\n'<<((void*)&foo)<<'\n'<<foo()<<std::endl; std::cout<<"boo"<<'\n'<<((void*)&boo)<<'\n'<<boo()<<std::endl; }
但是,例如在gcc https://godbolt.org/z/dQXmHm上,优化级别函数可能是内联的。
5条答案
按热度按时间bxpogfeg1#
在c++23之前的lambda中,无法直接获取lambda对象的地址。
现在,碰巧的是,这是非常有用的,最常见的用法是为了递归。
y_combinator
来自于那些在定义之前你不能谈论自己的语言,它可以很容易地在c++中实现:现在你可以这样做:
有几个有用的变体。我发现特别有用的一个变体是:
其中,可以调用传递的
self
,而无需将self
作为第一个参数传递。我相信第二个匹配真实的的y组合子(也就是不动点组合子),你想要哪个取决于你所说的“lambda地址”。
还有这样一句精辟的话:
其返回STD函数。
在c++23中,在lambda中访问
this
变得更加容易:你可以把第一个参数标记为
this
,它就变成了它自己,它甚至可以使用重载集合技巧,其中self
是最容易推导的类型。ubby3x7f2#
这不是直接可能的。
然而,lambda捕获是类,对象的地址与它的第一个成员的地址一致。因此,如果你按值捕获一个对象作为第一个捕获,第一个捕获的地址对应于lambda对象的地址:
输出:
或者,您可以创建一个decorator design pattern lambda,将对lambda捕获的引用传递到其调用操作符中:
wz3gfoph3#
解决这个问题的一个方法是用一个手写的函子类来替换lambda,这也是lambda的本质。
然后,你可以通过
this
得到地址,甚至不用把函子赋给变量:输出:
这样做的优点是100%可移植,并且非常容易推理和理解。
hwamh0ep4#
捕获lambda:
kyvafyod5#
这是可能的,但高度依赖于平台和编译器优化。
在我所知道的大多数架构中,都有一个寄存器叫做指令指针,这个解决方案的要点是当我们在函数内部时提取它。
在amd64上,下面的代码应该会给予你一个接近函数的地址。
但是,例如在gcc https://godbolt.org/z/dQXmHm上,优化级别函数可能是内联的。