1. ホーム
  2. c++

[解決済み】キャプチャしたラムダを関数ポインタとして渡す

2022-03-30 05:04:32

質問

ラムダ関数を関数ポインタとして渡すことは可能でしょうか?もし可能であれば、コンパイルエラーが発生するので、何か間違ったことをしているに違いありません。

次のような例を考えてみましょう。

using DecisionFn = bool(*)();

class Decide
{
public:
    Decide(DecisionFn dec) : _dec{dec} {}
private:
    DecisionFn _dec;
};

int main()
{
    int x = 5;
    Decide greaterThanThree{ [x](){ return x > 3; } };
    return 0;
}

私が をコンパイルしてみる。 を実行すると、以下のようなコンパイルエラーが発生します。

In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9:  note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5:   note: Decide::Decide(DecisionFn)
9:5:   note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7:   note: constexpr Decide::Decide(const Decide&)
6:7:   note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7:   note: constexpr Decide::Decide(Decide&&)
6:7:   note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'

このエラーメッセージを消化するのは大変ですが、私が得たものは、ラムダが constexpr ということは、関数ポインタとして渡すことはできないのでしょうか?試しに x constexprも同様ですが、それは解決しないようです。

どのように解決するのですか?

ラムダが関数ポインタに変換されるのは、そのラムダが C++11標準ドラフト セクション 5.1.2 [expr.prim.lambda]です。 は言う ( 強調 ):

ラムダ式のクロージャ型 ラムダキャプチャーがない場合 があります。 public 非仮想的な非明示的な const ポインタへの変換関数 関数に クロージャのパラメータと戻り値の型が同じであること。 型の関数呼び出し演算子です。この変換演算子が返す値 関数は、呼び出されたときに、その関数のアドレスでなければならない。 クロージャ型の関数呼び出し演算子を呼び出したのと同じ効果が得られる。

なお、cppreferenceでも、この件に関しては ラムダ関数 .

そこで、次のような代替案が考えられます。

typedef bool(*DecisionFn)(int);

Decide greaterThanThree{ []( int x ){ return x > 3; } };

で、これもそうでしょう。

typedef bool(*DecisionFn)();

Decide greaterThanThree{ [](){ return true ; } };

というように 5gon12eder が指摘するように std::function ただし std::function はヘビーウェイト というわけで、コスト度外視のトレードオフではありません。