如何在lambda本身中获取C++lambda函数的地址

How to get the address of a C++ lambda function within the lambda itself?

本文关键字:C++lambda 获取 函数 地址 lambda      更新时间:2023-10-16

我正试图弄清楚如何在lambda函数本身中获取地址。这是一个示例代码:

[]() {
std::cout << "Address of this lambda function is => " << ????
}();

我知道我可以在一个变量中捕获lambda并打印地址,但我想在执行这个匿名函数时就地执行。

有没有更简单的方法?

在c++23之前的lambda中,无法直接获取lambda对象的地址。

现在,碰巧的是,这通常很有用。最常见的用法是为了递归。

y_combinator来自于这样的语言:在你被定义之前,你不能谈论自己。它可以很容易地在c++中实现:

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)... );
}
};

其中传递的CCD_ 2可以在不传递CCD_。

第二个匹配真实的y组合子(也就是不动点组合子(,我相信。你想要哪个取决于你所说的"lambda地址"是什么意思。

还有一个精辟的例子:

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变得更容易:

auto fib = [](this auto& self, int n) {
if (n < 2) return n;
return self(n-1) + self(n-2);
};

您可以将第一个参数标记为this,它就变成了自己。它甚至可以使用重载集技巧,其中self是最推导的类型。

这不是直接可能的。

但是,lambda捕获是类,对象的地址与其第一个成员的地址一致。因此,如果按值捕获一个对象作为第一次捕获,则第一次捕获的地址对应于lambda对象的地址:

int main() {
int i = 0;
auto f = [i]() { printf("%pn", &i); };
f();
printf("%pn", &f);
}

输出:

0x7ffe8b80d820
0x7ffe8b80d820

或者,您可以创建一个装饰器设计模式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("%pn", &that); });
f();
}

解决此问题的一种方法是用手工编写的函子类替换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%可移植的,并且非常容易推理和理解。

捕获lambda:

std::function<void ()> fn = [&fn]() {
std::cout << "My lambda is " << &fn << std::endl;
}

这是可能的,但在很大程度上取决于平台和编译器的优化。

在我所知道的大多数体系结构中,都有一个称为指令指针的寄存器。这个解决方案的要点是当我们在函数内部时提取它。

在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具有CCD_ 8的优化级别函数可以被内联。