web-dev-qa-db-ja.com

ラムダへの関数ポインタを取得していますか?

C++でラムダへの関数ポインターを取得できるようにしたい。

できます:

int (*c)(int) = [](int i) { return i; };

そして、もちろん、次のように機能します-関数ポインターを作成していなくても。

auto a = [](int i) { return i; };

ただし、次のとおりです。

auto *b = [](int i) { return i; };

GCCで次のエラーが発生します。

main.cpp: In function 'int main()':
main.cpp:13:37: error: unable to deduce 'auto*' from '<lambda closure object>main()::<lambda(int)>{}'
     auto *b = [](int i) { return i; };
                                      ^
main.cpp:13:37: note:   mismatched types 'auto*' and 'main()::<lambda(int)>'

ラムダを問題なく関数ポインターに変換できることはarbitrary意的と思われますが、コンパイラはauto *を使用して関数タイプを推測し、ポインターを作成できません。特に、unique, lambda typeを暗黙的に関数ポインターに変換できる場合:

int (*g)(int) = a;

http://coliru.stacked-crooked.com/a/2cbd62c8179dc61b に上記の例を含む小さなテストベッドを作成しました。この動作は、C++ 11とC++ 14で同じです。

35
oconnor0

これは失敗します:

auto *b = [](int i) { return i; };

ラムダはポインターではないからです。 autoは変換を許可しません。ラムダはポインターであるものに変換可能ですが、それはあなたのために行われません-あなたはそれを自分でしなければなりません。キャストの有無:

auto *c = static_cast<int(*)(int)>([](int i){return i;});

または 一部のソーサリー

auto *d = +[](int i) { return i; };
44
Barry

特に、一意のラムダ型を暗黙的に関数ポインターに変換できる場合:

ただし、「関数ポインタ」に変換することはできません。 specific関数シグネチャへのポインタにのみ変換できます。これは失敗します:

_int (*h)(float) = a;
_

なぜそれが失敗するのですか?ここではaからhへの有効な暗黙的な変換がないためです。

ラムダの変換はコンパイラの魔法ではありません。標準では、非キャプチャ、非ジェネリックラムダのラムダクロージャタイプには、そのoperator()オーバーロードのシグネチャに一致する関数ポインタの暗黙的な変換演算子があると単純に述べられています。 aからint (*g)(int)を初期化するための規則では、暗黙的な変換の使用が許可されているため、コンパイラーはその演算子を呼び出します。

autoは、暗黙的な変換演算子の使用を許可しません。タイプをそのまま使用します(もちろん参照を削除します)。 _auto*_は暗黙的な変換も行いません。では、なぜユーザー定義型ではなくラムダクロージャーの暗黙的な変換を呼び出すのでしょうか?

7
Nicol Bolas

ラムダコードは、これが機能しないのと同じ理由で機能しません。

_struct foo {
  operator int*() const {
    static int x;
    return &x;
  }
};

int* pint = foo{};
auto* pint2 = foo{}; // does not compile
_

あるいは:

_template<class T>
void test(T*) {};
test(foo{});
_

ラムダには、fooと同様に、暗黙的に(特定の)関数ポインターに変換する演算子があります。

autoは変換を行いません。今まで。自動は、その型が推定されるテンプレート関数に対する_class T_パラメーターのように動作します。

右側の型はポインターではないため、_auto*_変数を初期化するために使用することはできません。

ラムダは関数ポインターではありません。ラムダは_std::function_ sではありません。これらは自動記述された関数オブジェクト(operator()を持つオブジェクト)です。

これを調べてください:

_void (*ptr)(int) = [](auto x){std::cout << x;};
ptr(7);
_

gccでコンパイルおよび動作します(拡張機能であるかどうかは定かではありませんが、今考えています)。しかし、auto* ptr = [](auto x){std::cout << x;}は何をすべきでしょうか?

ただし、単項_+_は、ポインターに対して機能する(およびそれらに対してほとんど何もしない)演算子ですが、fooまたはラムダでは機能しません。

そう

_auto* pauto=+foo{};
_

そして

_auto* pfun=+[](int x){};
_

両方とも魔法のように機能します。