C++14に追加された新機能に、ジェネリックlambdaがある。
lambda式は便利だ。lambda式が便利な理由は、簡潔に書けるからだ。しかし、C++11のlambda式は、引数の型を書かなければならないので、十分に簡潔に書くことはできない。
struct very_long_name
{
int x ;
double y ;
std::string z ;
} ;
void sort( std::vector<very_long_name> & v )
{
std::sort( v.begin(), v.end(),
[]( very_long_name & a, very_long_name & b )
{ // 複数のデータメンバーを正しく比較する方法を覚えておくと、
// いつかためになる。
return std::tie( a.x, a.y, a.z ) < std::tie( b.x, b.y, b.z ) ;
} ) ;
}
引数の型ぐらい、コンパイラーが何とかしてほしいものである。そこで、C++14には、引数の型を書かずにすむ、ジェネリックlambdaが追加された。
std::sort( v.begin(), v.end(),
[]( auto & a, auto & b )
{
return std::tie( a.x, a.y, a.z ) < std::tie( b.x, b.y, b.z ) ;
} ) ;
これだけでは、単に引数の型を省略出来るだけに見えるかもしれない。以下の例で、ジェネリックlambdaの素晴らしさがわかるだろう。
template < typename print >
void f( print p )
{
p( 42 ) ;
p( 3.14 ) ;
p( "hello" ) ;
}
int main()
{
f( []( auto && elem ) { std::cout << elem << '\n' ; } ) ;
}
ジェネリックlambdaは、テンプレートのようにどんな型でも受け付ける。
lambda式は魔法ではなく、クロージャーオブジェクトと呼ばれる、単なる関数オブジェクトを返すシンタックスシュガーに過ぎない。ジェレリックlambda式も同じだ。テンプレートのようにと書いたが、実際、メンバーテンプレートを持つ関数オブジェクトのシンタックスシュガーに過ぎない。例えば、上記のlambda式によって生成されるクロージャーオブジェクトは、以下のようになる。
struct closure_object
{
template < typename T >
void operator ( T && elem ) const
{ std::cout << elem << '\n' ; }
} ;
メンバーテンプレートを使えば、printに与える適切な関数オブジェクトを記述することができるが、C++11のlambda式には、これを可能にする文法がなかった。C++14で、ジェネリックlambdaとして追加された。
なおこの機能はGCC 4.9とClang 3.4で実装されている。
Clang - C++1z, C++14, C++11 and C++98 Status
C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)
See Also:
本の虫: C++14の新機能: decltype(auto)
本の虫: C++14の新機能: 初期化lambdaキャプチャー
ドワンゴ広告
この記事はドワンゴ勤務中に書かれた。
次の論文集がでるのは10月半ばになるので、それまではC++の解説記事を書く。
ドワンゴは本物のC++プログラマーを募集しています。
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0
std::tieは勉強になりました。
ReplyDelete自分、クイックソートを理解してないので手書きで書けないんですよね。いつもバブルソートで逃げてます。
しかし、これからはtieがあるので逃げずに済みそうです。
10月の論文もよろしくお願いします。
自分の一次ソースここですから。
ジェネリックでキャプチャしないlambda式を関数ポインタに変換するにはどうすれば良いのでしょうか?
ReplyDelete