web-dev-qa-db-ja.com

C ++ 11で「auto」で推定された場合のラムダのタイプは何ですか?

ラムダの型は関数ポインターであるという認識がありました。次のテストを実行すると、間違っていることがわかりました( demo )。

#define LAMBDA [] (int i) -> long { return 0; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok
  assert(typeid(pFptr) == typeid(pAuto));  // assertion fails !
}

上記のコードにはポイントがありませんか?そうでない場合、typeofキーワードで演deされるときのauto a lambda式は何ですか?

124
iammilind

ラムダ式の型は指定されていません。

しかし、それらは一般にファンクターにとって単なる構文上の砂糖です。ラムダはファンクターに直接変換されます。 _[]_内のすべてのものは、コンストラクターパラメーターとファンクターオブジェクトのメンバーになり、_()_内のパラメーターはファンクターのoperator()のパラメーターになります。

変数をキャプチャしないラムダ(_[]_の中に何もない)変換可能は関数ポインターになります(MSVC2010は、コンパイラーである場合、これをサポートしませんが、この変換は標準)。

しかし、ラムダの実際の型は関数ポインターではありません。不特定のファンクタータイプです。

124
jalf

これは、関数呼び出し演算子をオーバーロードする一意の名前のない構造です。ラムダのすべてのインスタンスは、新しい型を導入します。

非キャプチャラムダの特殊なケースでは、構造体はさらに関数ポインターへの暗黙的な変換を行います。

98
avakar

[C++11: 5.1.2/3]:lambda-expressionのタイプ(クロージャーオブジェクトのタイプでもあります)一意の名前のない非ユニオンクラスタイプクロージャタイプと呼ばれる—プロパティについては以下で説明します。このクラスタイプは集約ではありません(8.5.1)。クロージャタイプは、対応するlambda-expressionを含む最小のブロックスコープ、クラススコープ、またはネームスペーススコープで宣言されます。 [..]

この句は、このタイプのさまざまなプロパティをリストします。ハイライトは次のとおりです。

[C++11: 5.1.2/5]:lambda-expressionのクロージャー型には、パラメーターと戻り値型を持つpublic inline関数呼び出し演算子(13.5.4)がありますlambda-expressionparameter-declaration-clauseによって記述されますおよびtrailing-return-type[..]

[C++11: 5.1.2/6]:lambda-expressionのクロージャータイプ(lambda-captureは、クロージャー型の関数呼び出し演算子と同じパラメーターと戻り型を持つ関数へのポインターへのパブリック非仮想非明示const変換関数を持っています。この変換関数によって返される値は、呼び出されたときに、クロージャータイプの関数呼び出し演算子を呼び出すのと同じ効果を持つ関数のアドレスでなければなりません。

この最後の一節の結果は、変換を使用した場合、LAMBDApFptrに割り当てることができるということです。

#include <iostream>
#include <typeinfo>

#define LAMBDA [] (int i)->long { return 0l; }
int main ()
{
  long (*pFptr)(int) = LAMBDA;  // ok
  auto pAuto = LAMBDA;  // ok

  std::cout<<typeid( *pAuto ).name() << std::endl;
  std::cout<<typeid( *pFptr ).name() << std::endl;

  std::cout<<typeid( pAuto ).name() << std::endl;
  std::cout<<typeid( pFptr ).name() << std::endl;
}

関数の型は確かに同じですが、ラムダは新しい型(ファンクターのような)を導入します。

2
BЈовић

また、ラムダは関数ポインターに変換可能であることに注意する必要があります。ただし、typeid <>は、ラムダとジェネリック関数ポインタとでは異なる非trvialオブジェクトを返します。したがって、typeid <>のテストは有効な仮定ではありません。一般に、C++ 11では、特定の型がターゲット型に変換可能かどうかに関係なく、型の指定について心配する必要はありません。

1
Syed Raihan

boost :: bindオブジェクトをクラスメンバーとして格納するにはどうすればよいですか? からの実用的な解決策は、boost::function<void(int)>またはstd::function<void(int)>を試してください。

0
Gabriel