それ自体の中でラムダ関数のアドレスを取得する方法を理解しようとしています。これがサンプルコードです:
[]() {
std::cout << "Address of this lambda function is => " << ????
}();
ラムダを変数にキャプチャしてアドレスを出力できることはわかっていますが、この無名関数が実行されているときにそれを実行したいと考えています。
これを行う簡単な方法はありますか?
this
。
それ自体の中でラムダ関数のアドレスを取得する方法を理解しようとしています。これがサンプルコードです:
[]() {
std::cout << "Address of this lambda function is => " << ????
}();
ラムダを変数にキャプチャしてアドレスを出力できることはわかっていますが、この無名関数が実行されているときにそれを実行したいと考えています。
これを行う簡単な方法はありますか?
this
。
回答:
直接行うことはできません。
ただし、ラムダキャプチャはクラスであり、オブジェクトのアドレスは最初のメンバーのアドレスと一致します。したがって、最初のキャプチャとして1つのオブジェクトを値でキャプチャする場合、最初のキャプチャのアドレスはラムダオブジェクトのアドレスに対応します。
int main() {
int i = 0;
auto f = [i]() { printf("%p\n", &i); };
f();
printf("%p\n", &f);
}
出力:
0x7ffe8b80d820
0x7ffe8b80d820
または、ラムダキャプチャへの参照を呼び出し演算子に渡すデコレータデザインパターンラムダを作成できます。
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();
}
ラムダ内のラムダオブジェクトのアドレスを直接取得する方法はありません。
さて、これが起こると、これは非常に便利です。最も一般的な使用は、再帰するためです。
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)... );
}
};
今これを行うことができます:
y_combinator{ [](auto& self) {
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
ます。
2番目は、実際のyコンビネーター(別名、固定小数点コンビネーター)と一致すると私は信じています。どちらを使用するかは、「ラムダのアドレス」の意味によって異なります。
F
が標準レイアウトでない場合y_combinator
、そうではないので、正気の保証は提供されません。
これを解決する1つの方法は、ラムダを手書きのファンクタクラスに置き換えることです。それはラムダが本質的に内部にあるものでもあります。
次に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%移植可能であり、推論と理解が非常に簡単であるという利点があります。
struct { void operator()() { std::cout << "Address of this functor is => " << this << '\n'; } } f;
ラムダをキャプチャします。
std::function<void ()> fn = [&fn]() {
std::cout << "My lambda is " << &fn << std::endl;
}
std::function
ただし、ここではaの柔軟性は必要ありませんが、かなりのコストがかかります。また、そのオブジェクトをコピー/移動すると壊れます。
それは可能ですが、プラットフォームとコンパイラの最適化に大きく依存します。
私が知っているほとんどのアーキテクチャでは、命令ポインタと呼ばれるレジスタがあります。このソリューションのポイントは、関数の内部にいるときに抽出することです。
amd64の場合次のコードは、関数1に近いアドレスを提供します。
#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では、-O3
最適化レベル関数がインライン化される場合があります。
thread_local
ストレージ期間のオブジェクトのアドレスを導出することはできません。ここで取得しようとしているのは、オブジェクトではなく関数の戻りアドレスです。しかし、コンパイラが生成した関数プロローグがスタックにプッシュし、スタックポインタを調整してローカル変数用のスペースを作るため、それでも機能しません。