C ++ 11で「auto」を使用して推定されるときのラムダのタイプは何ですか?


141

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

#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推定されるときのラムダ式は何autoですか?


8
「ラムダのタイプは関数ポインタです」–これは非効率であり、ラムダの全体のポイントを逃します。
Konrad Rudolph、

回答:


144

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

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

変数をキャプチャしないラムダ([]「」内には何もない)、関数ポインタに変換できます(コンパイラの場合、MSVC2010はこれをサポートしていませんが、この変換は標準の一部です)。

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


1
MSVC2010は関数ポインターへの変換をサポートしていませんが、MSVC11はサポートしています。blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx
KindDragon

17
「ファンクタの単なる構文糖」の+1。これを覚えておけば、混乱を避けることができます。
ベン

4
ファンクターはoperator()基本的に、stackoverflow.com
questions / 356950 / c

107

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

非キャプチャラムダの特別なケースでは、構造体に関数ポインタへの暗黙的な変換が追加されています。


2
素敵な答え。私よりはるかに正確です。+1 :)
jalf

4
ユニシティ部分の+1、それは最初は非常に驚き、注目に値します。
Matthieu M.

それは本当に重要なことではありませんが、型は本当に名前が付けられていませんか、それともコンパイル時まで名前が付けられていないだけですか?IOW、コンパイラが決めた名前を見つけるためにRTTIを使用できますか?
ベン

3
@Ben、それは無名であり、C ++言語に関する限り、「コンパイラが決定する名前」といったものはありません。の結果type_info::name()は実装で定義されるため、何でも返す可能性があります。実際には、コンパイラーはリンカーのために型に名前を付けます。
avakar 2011年

1
最近、この質問をするとき、ラムダの型は名前あると私はよく言っています。コンパイラはそれを知っています。
Andre Kostur

24

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

この節では、このタイプのさまざまなプロパティをリストします。ここにいくつかのハイライトがあります:

[C++11: 5.1.2/5]:ラムダ式のクロージャタイプには、パブリックinline関数呼び出し演算子(13.5.4)があり、そのパラメータと戻り値のタイプは、ラムダ式parameter-declaration-clausetrailing-return-typeによってそれぞれ記述されます。[..]

[C++11: 5.1.2/6]:以下のための閉鎖型のラムダ式なしでラムダ・キャプチャーは、閉鎖型の関数呼び出し演算子と同じパラメータと戻り値の型を持つ関数へのポインタへの公共非仮想非明示的なconstの変換機能を持っています。この変換関数によって返される値は、呼び出されたときに、クロージャタイプの関数呼び出し演算子を呼び出すのと同じ効果を持つ関数のアドレスになります。

この最後の通路の結果は、あなたが変換を使用した場合、あなたが割り当てることができるだろう、ということであるLAMBDApFptr


3
#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;
}

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


すでにこの方法を使用している場合は、CXXABIのアンマングルアプローチをお勧めます。代わりに、私は通常、のようにを使用し__PRETTY_FUNCTION__template<class T> const char* pretty(T && t) { return __PRETTY_FUNCTION__; }混雑し始めたら余分なものを取り除きます。テンプレートの置換で示されている手順を確認したいと思います。がない場合__PRETTY_FUNCTION__、MSVCなどの代替手段がありますが、CXXABIが必要なのと同じ理由で、結果は常にコンパイラに依存します。
John P

1

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


それは公平なことですが、印刷タイプは正しいタイプに到達するのに大いに役立ちます。タイプが変換可能であるが他の制約を満たさないケースをキャッチすることは言うまでもありません。(私は常に可能な限り制約を「修正」するようにプッシュしますが、そうしようとする誰かは、開発中に彼らの仕事を示すためのより多くの理由があります。)
John P

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.