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

hmae6n7t  于 2023-02-26  发布在  其他
关注(0)|答案(5)|浏览(243)

我想弄清楚如何获取lambda函数自身的地址。下面是一个示例代码:

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

我知道我可以在变量中捕获lambda并打印地址,但是我想在执行这个匿名函数的时候就这样做。
有没有更简单的方法?

bxpogfeg

bxpogfeg1#

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

其中,可以调用传递的self,而无需将self作为第一个参数传递。
我相信第二个匹配真实的的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是最容易推导的类型。

ubby3x7f

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();
}
wz3gfoph

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%可移植,并且非常容易推理和理解。

hwamh0ep

hwamh0ep4#

捕获lambda:

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

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上,优化级别函数可能是内联的。

相关问题