SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

C++0xの新機能「ラムダ式」を次期Visual Studioでいち早く試す

Visual Studio 2010 β1で遊んでみた

キャプチャ(capture)とは

 ラムダ式中で使えるオブジェクトは引数で受け渡された変数だけでなく、例えば上記のようにstd::coutのようなグローバルなインスタンスが使えます。

 また、スコープ内のautomatic変数をラムダ式内部にキャプチャする(抱き込む)ことができます。この場合は[]内にキャプチャする変数名を指定します。

#include <iostream>

int main() {
  int n = 123;
  auto capt_print =
    [n]() { std::cout << n << " をキャプチャしています\n"; };
  capt_print(); // "123 をキャプチャしています"
}

 キャプチャは、ラムダ式が定義された時点で行われます。そのため、このサンプルにおいてラムダ式の定義の後でnの値を変更しても、既にキャプチャされているので、capt_print()は、"123 をキャプチャしています"を出力します。

int n = 123;
auto capt_print =
  [n]() { std::cout << n << " をキャプチャしています\n"; };
n = 456; // ラムダ式の評価には影響を及ぼさない
capt_print(); // "123 をキャプチャしています"

 ここで、試しにラムダ式の評価の度にnをインクリメントしてみましょう。キャプチャされた変数をラムダ式内部で変更するには、ラムダ引数の後にmutableを指定します。

#include <iostream>

int main() {
  int n = 123;
  auto capt_print =
    [n]() mutable { std::cout << n++ << " をキャプチャしています\n"; };
  capt_print(); // "123 をキャプチャしています"
  capt_print(); // "124 をキャプチャしています"
  capt_print(); // "125 をキャプチャしています"
  std::cout << "n = " << n << std::endl;
}

 …おや? キャプチャされた変数nは、ラムダ式の評価後に変更されていませんね。これは変数nの「値」がキャプチャされたからです(値キャプチャ)。ラムダ式の評価によってキャプチャされた変数を変更したいときは。「参照キャプチャ」を用います。

参照キャプチャの例
#include <iostream>

int main() {
  int n = 123;
  auto capt_print =
  // ↓この'&'が参照キャプチャを意味する
    [&n]() { std::cout << n++ << " をキャプチャしています\n"; };
  capt_print(); // "123 をキャプチャしています"
  capt_print(); // "124 をキャプチャしています"
  capt_print(); // "125 をキャプチャしています"
  std::cout << "n = " << n << std::endl; // "n = 126"
  n = 456;
  capt_print(); // "456 をキャプチャしています"
  std::cout << "n = " << n << std::endl;// "n = 457"
}

 ラムダ式に複数の変数をキャプチャするときは、それらを[]内に並べます。

#include <iostream>
#include <algorithm>

int main() {
  int target = 5;
  int count;
  // targetより小さい要素の個数を数える
  auto count_less =
    [&count,target](int x) { if ( x < target) ++count; };

  int data[] = { 1, 3, 5, 7, 2, 4, 6, 8 };
  count = 0;
  std::for_each( data, data+8, count_less);
  std::cout << target << " より小さな数が " << count << " 個あります" << std::endl;
}

 '['の直後に'='を置くと「デフォルトで値キャプチャ」、'&'を置くと「デフォルトで参照キャプチャ」を意味します。従って、上記の[&count,target]は、[=,&count]あるいは[&,target]と書いてもかまいませんし、すべての変数が値キャプチャあるいは参照キャプチャであるなら、それぞれ[=][&]と略記できます。

 また、[]内にthisを指定することで、クラスのメンバ関数を呼び出すこともできます。

#include <iostream>
#include <vector>
#include <algorithm>

template<typename T>
class numbers {
  std::vector<T> data;
  void print_one(T x) const { std::cout << x << ' '; }
public:
  void add(T x) { data.push_back(x); }
  void print_all() const {
    std::for_each(data.begin(), data.end(), [this](T x) { print_one(x); });
  }
};

int main() {
  numbers<int> ints;
  ints.add(1); ints.add(2); ints.add(3);
  ints.print_all(); // "1 2 3 "
}

まとめ

 C++0Xでは、より楽に効率的なコードを書けるように多くの新たな機能が追加されました。今回紹介したラムダ式もその1つです。ラムダ式のおかげで、小さな関数オブジェクトをいちいち定義することなく、“使い捨て”感覚で定義できます。これは、従来のコーディング・スタイルを大きく変えるものになりそうです。特に<algorithm>との組み合わせによって、今までfor-loopで書いていたものの多くが簡略化されるでしょう。

 Visual Studio 2010βでほんの少し未来を覗いてみませんか?

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/4035 2009/09/10 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー