この回答に続いて、ラムダの有効期間のルールと、自動変換によって作成される関数ポインターの有効期間との関係について疑問に思っています。ラムダの寿命についていくつかの質問があります (例: hereおよびhere )。その場合、答えは「完全なファンクター オブジェクトを自分で記述したのとまったく同じように動作する」ですが、関数ポインターへの変換についてはどちらも対処していません。特別なケース。
私の懸念を説明するこの小さな実用的な例をまとめました。
#include <iostream>
typedef int (*func_t)(int);
// first case
func_t retFun1() {
static auto lambda = [](int) { return 1; };
// automatically converted to func_t
return lambda;
}
// second case
func_t retFun2() {
// no static
auto lambda = [](int) { return 2; };
// automatically converted to func_t and
// the local variable lambda reaches the end of its life
return lambda;
}
int main() {
const int a = retFun1()(0);
const int b = retFun2()(0);
std::cout << a << "," << b << std::endl;
return 0;
}
これは両方のケースで明確に定義されていますか? それともretFun1()
?問題は、「関数ポインターが指す関数は、ファンクター オブジェクト自体を呼び出す必要があるか、それとも別の関数で本体を再実装する必要があるか?」です。どちらも理にかなっていますが、関数ポインターへの変換には特にキャプチャーのないラムダが必要であるという事実は、実際には後者である可能性があることを示唆しています。
別の言い方をすれば、コンパイラがそのようなラムダを実装したいと思うかもしれない少なくとも 2 つの賢明な方法を見ることができます。可能な合法的な実装の 1 つは、コンパイラが次のようなコードを合成することです。
func_t retFun3() {
struct __voodoo_magic_lambda_implementation {
int operator()(int) const {
return 3;
}
static int plainfunction(int) {
return 3;
}
operator func_t() const {
return plainfunction;
}
} lambda;
return lambda;
}
その場合、 のバリアントstatic
と非static
バリアントの両方でretFun
問題ありません。ただし、コンパイラが次のようなラムダを実装することも合法である場合:
static int __voodoo_impl_function(int x);
static struct __voodoo_maigc_impl2 {
int operator()(int) const {
return 4;
}
operator func_t() const {
return __voodoo_impl_function;
}
} *__magic_functor_ptr;
static int __voodoo_impl_function(int x) {
return (*__magic_functor_ptr)(x);
}
func_t retFun4() {
__voodoo_maigc_impl2 lambda;
// non-static, local lifetime
__magic_functor_ptr = λ //Or do the equivalent of this in the ctor
return lambda;
}
その後retFun2()
は未定義の動作です。