LoginSignup
34
33

More than 5 years have passed since last update.

ラムダ式考察

Last updated at Posted at 2015-04-04

ラムダ式とは

C++のラムダ式は、最小の関数オブジェクトを作るシンタックスシュガーです。

キャプチャ

キャプチャとは、ラムダ式の外の変数などをラムダ式の中で使えるようにすることです。
具体的には、メンバ変数として持たせることになります。
以下はコピーキャプチャの例ですが、参照キャプチャも大差はありません。
ただし、コピーキャプチャは内部にコピーを持つので、大きな構造体なんかをコピーキャプチャで持つとsizeof( lambda )が膨れ上がります。当然ですが、キャプチャする際にコピーが行われますので、コピーが重たいオブジェクトをコピーキャプチャするのはおすすめできません。
注意するところは、ラムダ式のoperator()はconst修飾されていますので、コピーキャプチャの場合const修飾されていないメンバ関数を呼び出すことができません。
ラムダ式のconst修飾を外すには、mutableを使います。[]() mutable ->void {};

[s]{ s.memfun(); };

class _Lambda
{
public:
  _Lambda( const something& s ) : s( s ) {}
  void operator()() const { s.memfun(); }
private:
  something s;
};
_Lambda lambda( s );

ムーブキャプチャ

C++14から、キャプチャに初期化子と別名を与えることができるようになりました。


[ vvlln = very_very_long_long_name ]{ vvlln.dosomething(); };
[ N_plus_1 = n + 1 ] { std::cout << N_plus_1 << std::endl; };

これにより、C++11ではできなかったムーブキャプチャができるようになりました。
[ something = std::move( something ) ] { something.dosomething(); };

ラムダ式の型

ラムダ式の型はコンパイラが独自に生成する一意の型というかたちになっています。
ラムダ式の型はdecltypeなどで取得することはできないので、テンプレートクラスにラムダ式の型を与えたい場合などは、テンプレート関数で推論してあげるのみとなります。しかし、テンプレートメタプログラミングにラムダ式の型を使うことはできません。
また、ラムダ式はコピー、ムーブ以外のコンストラクタが基本的にdeleteされている(operator= はコピー/ムーブも含めて削除されている)ので、継承してもインスタンス化することはできません。


template < class _Base >
struct test0 : public _Base
{
};

template < class Ty >
test0 < Ty > test1( const Ty & )
{
  return test0 < Ty >();  // test0() is implicitly deleted!
}

int main( int arg, char** argv )
{
  auto _Test = test1( [] { std::cout << "abc" << std::endl; } );
  _Test();
}

ジェネリックラムダ

// 以降、5/9に追記と修正を加えました。

C++14から、ラムダ式は引数にautoを受けることができるようになりました。
auto lambda0 = []( auto a, auto b ) { return a + b; };

このジェネリックラムダはメンバ関数テンプレートによって実現されます。
上のラムダ式では、コンパイラは以下のようなクラスを生成するでしょう。


class _Lambda0
{
public:
  template< class Ty1, class Ty2 >
  auto operator()( Ty1 a, Ty2 b ) const { return a + b; }
};
_Lambda0 lambda0;

戻り値はautoです。decltype(auto)とは異なることに注意してください。


auto lambda1 = []( auto& a ) { return a; }
auto lambda2 = []( auto& a ) -> decltype( auto ) { return ( a ); }
lambda1( value ) = 12; // oops, lvalue required
lambda2( value ) = 13; // OK, the return type is int&

また、ジェネリックラムダはテンプレートによって実現されるので、当然可変長テンプレート引数を受けることもできます。とても便利ですね。


auto lambda3 =
  []( auto func, auto&&... args ) -> decltype( auto )
  {
    return func( std::forward < decltype( args ) >( args )... );
  };

真面目に書くと大変です。なお、mutableはsth.dosomethingがconst修飾されていなくても呼べるようにつけています。

予定していたよりもだらだらと追記をしてしまいました。
ジェネリックラムダの詳細をかき足した形になります。

ラムダ式の戻り値の型

// 以降は10/25に追記した内容です。
ラムダ式の戻り値は明示しない限りautoになります(通常の関数にautoを指定できないC++11でも同様です)。

間違いなど気づいた点がございましたらコメントをください。

34
33
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
34
33