名前空間
変種
操作

ラムダ式 (C++11およびそれ以降)

提供: cppreference.com
< cpp‎ | language
 
 
C++言語
一般的なトピック
フロー制御
条件付き実行文
繰り返し文 (ループ)
ジャンプ文
関数
関数宣言
ラムダ関数宣言
inline 指定子
例外指定 (非推奨)
noexcept 指定子 (C++11)
例外
名前空間
指定子
decltype (C++11)
auto (C++11)
alignas (C++11)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点
文字 - 文字列 - nullptr (C++11)
ユーザ定義 (C++11)
ユーティリティ
属性 (C++11)
typedef 宣言
型エイリアス宣言 (C++11)
キャスト
暗黙の変換 - 明示的な変換
static_cast - dynamic_cast
const_cast - reinterpret_cast
メモリ確保
クラス
クラス固有の関数特性
特別なメンバ関数
テンプレート
その他
 
 

クロージャ (スコープ内の変数をキャプチャする能力を持つ無名の関数オブジェクト) を構築します。

目次

[編集] 構文

[ captures ] <tparams>(オプション)(c++20) ( params ) specifiers exception attr -> ret requires(オプション)(c++20) { body } (1)
[ captures ] ( params ) -> ret { body } (2)
[ captures ] ( params ) { body } (3)
[ captures ] { body } (4)
1) 完全な宣言。
2) const ラムダの宣言。 コピーによってキャプチャされたオブジェクトはラムダの本体内で const です。
3) 後置戻り値型の省略。 クロージャの operator() の戻り値の型は、戻り値の型が auto と宣言された関数であるかのように、 return 文から推定されます。
4) 引数リストの省略。 引数リストが () であるかのように、関数は引数を取りません。 この形式は constexpr、mutable、例外指定、属性、後置戻り値型のいずれも使用されない場合にのみ使用できます。

[編集] 説明

captures - ゼロ個以上のキャプチャのコンマ区切りのリスト。 オプションで capture-default を前置しても構いません。

キャプチャの詳細な説明については下記を参照してください。

ラムダ式は、変数が以下のいずれかの場合、それをキャプチャせずに使用することができます。

  • 非ローカル変数であるか、静的またはスレッドローカル記憶域期間を持つ (この場合、変数はキャプチャできません)。
  • 定数式で初期化された参照である。

ラムダ式は、変数が以下のいずれかの場合、それをキャプチャせずに、その値を読むことができます。

  • const かつ非 volatile な整数型または列挙型であり、定数式で初期化されている。
  • constexpr かつトリビアルにコピー構築可能である。

構造化束縛はキャプチャできません。

(C++17およびそれ以降)
<tparams>(C++20) - 総称ラムダ (後述の ClosureType::operator() を参照してください) のテンプレート引数に名前を提供するために使用される、 (山括弧で囲まれた) テンプレート引数リスト。 テンプレート宣言の場合と同様に、テンプレート引数リストには、テンプレートの実引数に対する制約を指定する、オプショナルな requires 節を続けることができます。 提供される場合、テンプレート引数リストは空にできません (<> は許されません)。
params - 名前付き関数の場合と同様の引数リスト。 ただしデフォルト引数は使用できません。 (C++14以前) 引数の型として auto が使用される場合、そのラムダは総称ラムダです。 (C++14およびそれ以降)
specifiers - オプショナルな指定子の並び。 以下の指定子が使用できます。
  • mutable: body がコピーによってキャプチャされた引数を変更することを可能とし、その非 const メンバ関数を呼べるようにします。
  • constexpr: 関数呼び出し演算子が constexpr 関数であることを明示的に指定します。 この指定子が存在しなくても、 constexpr 関数の要件をすべて満たす場合、関数呼び出し演算子はいずれにせよ constexpr になります。
(C++17およびそれ以降)
exception - そのクロージャ型の operator() に対する例外指定または noexcept 節を提供します。
attr - そのクロージャ型の operator() に対する属性指定を提供します。
ret - 戻り値の型。 存在しない場合は、関数の return 文によって暗黙に決定されます (または何の値も返さない場合は void になります)。
requires - そのクロージャ型の operator() に制約を追加します。
body - 関数の本体。

ラムダ式は、クロージャ型と呼ばれる、一意な無名の非共用体かつ非集成体であるクラス型の prvalue 式です。 このクラス型は (ADL の目的に対して) そのラムダ式を含む最も小さなブロックスコープ、クラススコープ、または名前空間スコープ内で宣言されます。 クロージャ型は以下のメンバを持ちます。

ClosureType::operator()(params)

ret operator()(params) const { body }
(キーワード mutable が使用されない場合)
ret operator()(params) { body }
(キーワード mutable が使用された場合)
template<template-params>
ret operator()(params) const { body }
(C++14およびそれ以降)
(総称ラムダの場合)
template<template-params>
ret operator()(params) { body }
(C++14およびそれ以降)
(総称ラムダかつキーワード mutable が使用された場合)

呼び出されると、ラムダ式の本体を実行します。 変数にアクセスすると、キャプチャされたコピー (コピーによってキャプチャされた場合) または元のオブジェクト (参照によってキャプチャされた場合) にアクセスします。 ラムダ式でキーワード mutable が使用されなかった場合、この関数呼び出し演算子は const 修飾され、コピーによってキャプチャされたオブジェクトはこの operator() の内側から変更できません。 この関数呼び出し演算子が volatile 修飾されることはなく、仮想関数となることもありません。

constexpr 関数の要件を満たす場合、この関数呼び出し演算子は常に constexpr です。 ラムダ宣言でキーワード constexpr が使用された場合も constexpr です。

(C++17およびそれ以降)

型が auto として指定されている params 内のすべての引数について、自動生成されたテンプレート引数が template-params に出現順に追加されます。 params の対応する関数メンバが関数パラメータパックの場合、自動生成されたテンプレート引数はパラメータパックであるかもしれません。

// 総称ラムダ。 operator() は2個の引数を持つテンプレートです。
auto glambda = [](auto a, auto&& b) { return a < b; };
bool b = glambda(3, 3.14); // OK
 
// 総称ラムダ。 operator() は1個の引数を持つテンプレートです。
auto vglambda = [](auto printer) {
    return [=](auto&&... ts) // 総称ラムダ。 ts はパラメータパックです。
    { 
        printer(std::forward<decltype(ts)>(ts)...);
        return [=] { printer(ts...); }; // 零項ラムダ (引数を取りません)。
    };
};
auto p = vglambda([](auto v1, auto v2, auto v3) { std::cout << v1 << v2 << v3; });
auto q = p(1, 'a', 3.14); // 1a3.14 を出力します。
q();                      // 1a3.14 を出力します。

ClosureTypeoperator() は明示的に実体化または明示的に特殊化することはできません。

(C++14およびそれ以降)

ラムダ定義が明示的なテンプレート引数リストを使用する場合は、そのテンプレート引数リストが operator() で使用されます。 型が auto として指定されている params 内のすべての引数について、追加の自動生成されたテンプレート引数がそのテンプレート引数リストの末尾に追加されます。

// 総称ラムダ。 operator() は2個の引数を持つテンプレートです。
auto glambda = []<class T>(T a, auto&& b) { return a < b; };
 
// 総称ラムダ。 operator() は1個のパラメータパックを持つテンプレートです。
auto f = []<typename ...Ts>(Ts&& ...ts) {
   return foo(std::forward<Ts>(ts)...);
};
(C++20およびそれ以降)

ラムダ式の例外指定 exception はこの関数呼び出し演算子または演算子テンプレートに適用されます。

名前探索および this ポインタの型と値の決定について、および非静的クラスメンバのアクセスについて、クロージャ型の関数呼び出し演算子の本体は、ラムダ式の文脈内とみなされます。

struct X {
    int x, y;
    int operator()(int);
    void f()
    {
        // 以下のラムダ式の文脈はメンバ関数 X::f です。
        [=]()->int
        {
            return operator()(this->x + y); // X::operator()(this->x + (*this).y)
                                            // this は X* 型です。
        };
    }
};

ClosureTypeoperator()friend 宣言で指定することはできません。

ダングリング参照

非参照エンティティが (明示的にまたは暗黙に) 参照によってキャプチャされ、そのクロージャオブジェクトの関数呼び出し演算子がそのエンティティの生存期間の終了後に呼ばれた場合は、未定義動作が発生します。 C++ のクロージャはキャプチャされた参照の生存期間を延長しません。

同じことがキャプチャされた this ポインタによって指されるオブジェクトの生存期間にも適用されます。

ClosureType::operator ret(*)(params)()

(キャプチャなし非総称ラムダの場合)
using F = ret(*)(params);
operator F() const;
(C++17以前)
using F = ret(*)(params);
constexpr operator F() const;
(C++17およびそれ以降)
(キャプチャなし総称ラムダの場合)
template<template-params> using fptr_t = /*see below*/;
template<template-params> operator fptr_t<template-params>() const;
(C++14およびそれ以降)
(C++17以前)
template<template-params> using fptr_t = /*see below*/;
template<template-params> operator fptr_t<template-params>() const;
(C++17およびそれ以降)

このユーザ定義変換関数はラムダ式のキャプチャリストが空の場合にのみ定義されます。 これはクロージャオブジェクトのパブリックで constexpr で (C++17およびそれ以降)非仮想で非 explicit な const noexcept (C++14およびそれ以降) メンバ関数です。

キャプチャのない総称ラムダは、関数呼び出し演算子テンプレートと同じ、自動生成されたテンプレート引数リストを持つ、ユーザ定義変換関数テンプレートを持ちます。 戻り値の型が空または auto の場合、それはこの関数テンプレートの特殊化に対する戻り値の型の推定によって取得され、それはすなわち、変換関数テンプレートに対するテンプレートの実引数推定によって取得されます。

void f1(int (*)(int)) {}
void f2(char (*)(int)) {}
void h(int (*)(int)) {} // #1
void h(char (*)(int)) {} // #2
auto glambda = [](auto a) { return a; };
f1(glambda); // OK
f2(glambda); // エラー、変換可能ではありません
h(glambda); // OK、 #2 は変換可能でないため #1 を呼びます
 
int& (*fpi)(int*) = [](auto* a)->auto& { return *a; }; // OK
(C++14およびそれ以降)

この変換関数によって返される値は C++ 言語リンケージを持つ関数へのポインタであり、呼ばれるとそのクロージャオブジェクトの関数呼び出し演算子を直接呼んだのと同じ効果を持ちます。

この関数は、その関数呼び出し演算子 (または総称ラムダの場合は特殊化) が constexpr であれば、 constexpr です。

auto Fwd= [](int(*fp)(int), auto a){return fp(a);};
auto C=[](auto a){return a;};
static_assert(Fwd(C,3)==3); // OK
auto NC=[](auto a){static int s; return a;};
static_assert(Fwd(NC,3)==3); // エラー、 s のためにこの特殊化は constexpr になれません。

クロージャオブジェクトの operator() が例外を投げない例外指定を持つ場合、この関数によって返されるポインタは noexcept 関数へのポインタ型です。

(C++17およびそれ以降)

ClosureType::ClosureType()

ClosureType() = delete;
(C++14以前)
ClosureType() = default;
(C++20およびそれ以降)(キャプチャが指定されない場合のみ)
ClosureType(const ClosureType& ) = default;
(C++14およびそれ以降)
ClosureType(ClosureType&& ) = default;
(C++14およびそれ以降)

クロージャ型は DefaultConstructible ではありません。 クロージャ型は削除されたデフォルトコンストラクタを持ちます (C++14以前)デフォルトコンストラクタを持ちません (C++14およびそれ以降)

(C++20以前)

captures が指定されない場合、そのクロージャ型はデフォルト化されたデフォルトコンストラクタを持ちます。 そうでなければ (capture-default は存在するけれども実際には何もキャプチャしていないという場合も含みます)、デフォルトコンストラクタを持ちません。

(C++20およびそれ以降)

コピーコンストラクタおよびムーブコンストラクタは暗黙に宣言され (C++14以前)デフォルト化されたものとして宣言され (C++14およびそれ以降)コピーコンストラクタおよびムーブコンストラクタの通常のルールに従って暗黙に定義されます。

ClosureType::operator=(const ClosureType&)

ClosureType& operator=(const ClosureType&) = delete;
(C++20以前)
ClosureType& operator=(const ClosureType&) = default;
ClosureType& operator=(ClosureType&&) = default;
(C++20およびそれ以降)
(キャプチャが指定されない場合のみ)
ClosureType& operator=(const ClosureType&) = delete;
(C++20およびそれ以降)
(otherwise)

コピー代入演算子は削除されたものとして定義されます (そしてムーブ代入演算子は宣言されません)。 クロージャ型は CopyAssignable ではありません。

(C++20以前)

captures が指定されない場合、クロージャ型はデフォルト化されたコピー代入演算子およびデフォルト化されたムーブ代入演算子を持ちます。 そうでなければ (capture-default が存在するけれども実際には何もキャプチャしていないという場合も含みます)、削除されたコピー代入演算子を持ちます。

(C++20およびそれ以降)

ClosureType::~ClosureType()

~ClosureType() = default;

デストラクタは暗黙に宣言されます。

ClosureType::Captures

T1 a;

T2 b;

...

ラムダ式のキャプチャがコピーによって何かを (キャプチャ節 [=] を用いて暗黙に、または & 文字を含まないキャプチャ ([a, b, c] など) を用いて明示的に) キャプチャした場合、そのクロージャ型は、そのようにキャプチャされたすべてのエンティティのコピーを保持する、未規定の順序で宣言された無名の非静的データメンバを持ちます。

初期化子なしのキャプチャに対応するそれらのデータメンバは、ラムダ式が評価されるときに直接初期化されます。 初期化子付きのキャプチャに対応するそれらのデータメンバは、その初期化子が要求する通りに (コピー初期化または直接初期化によって) 初期化されます。 配列がキャプチャされた場合、配列の要素はインデックスの昇順に直接初期化されます。 データメンバが初期化される順序はそれらが宣言された順序 (すなわち未規定) です。

それぞれのデータメンバの型は対応するキャプチャされたエンティティの型です。 ただしエンティティが参照型の場合は除きます。 関数への参照は その参照先の関数への左辺値参照としてキャプチャされ、オブジェクトへの参照はその参照先のオブジェクトのコピーとしてキャプチャされます。

参照によって (デフォルトキャプチャ [&] を用いて、または文字 & を用いて ([&a, &b, &c] など)) キャプチャされたエンティティの場合、クロージャ型に追加のデータメンバが宣言されるかどうかは未規定ですが、そのような追加のメンバは LiteralType を満たさなければなりません (C++17およびそれ以降)


ラムダ式は、未評価式テンプレートの実引数エイリアス宣言typedef 宣言、および関数の本体と関数のデフォルト引数を除く関数 (または関数テンプレート) 宣言内のいずれの場所でも使用することができません。

(C++20以前)

[編集] ラムダキャプチャ

captures はゼロ個以上のキャプチャのコンマ区切りのリストです。 オプションで capture-default を前置しても構いません。 キャプチャデフォルトは以下のいずれかのみです。

  • & (使用される自動変数を参照によって暗黙にキャプチャします)
  • = (使用される自動変数をコピーによって暗黙にキャプチャします)

現在のオブジェクト (*this) は、いずれかのキャプチャデフォルトが存在する場合、暗黙にキャプチャできます。 暗黙にキャプチャされた場合、それは、キャプチャデフォルトが = であっても、常に参照によってキャプチャされます。 キャプチャデフォルトが = のときの *this の暗黙のキャプチャは非推奨です。 (C++20およびそれ以降)

captures 内の個々のキャプチャの構文は以下の通りです。

identifier (1)
identifier ... (2)
identifier initializer (3) (C++14およびそれ以降)
& identifier (4)
& identifier ... (5)
& identifier initializer (6) (C++14およびそれ以降)
this (7)
* this (8) (C++17およびそれ以降)
1) 単純なコピーによるキャプチャ。
2) パック展開である単純なコピーによるキャプチャ。
3) 初期化子付きのコピーによるキャプチャ。
4) 単純な参照によるキャプチャ。
5) パック展開である単純な参照によるキャプチャ。
6) 初期化子付きの参照によるキャプチャ。
7) 単純な参照による現在のオブジェクトのキャプチャ。
8) 単純なコピーによる現在のオブジェクトのキャプチャ。

キャプチャデフォルトが & の場合、後続の単純なキャプチャは & で始まってはなりません。

struct S2 { void f(int i); };
void S2::f(int i)
{
    [&]{};          // OK、参照によるキャプチャデフォルト。
    [&, i]{};       // OK、参照によるキャプチャ、ただし i はコピーによってキャプチャされます。
    [&, &i] {};     // エラー、参照によるキャプチャがデフォルトのときの参照によるキャプチャ。
    [&, this] {};   // OK、 [&] と同等。
    [&, this, i]{}; // OK、 [&, i] と同等。
}

キャプチャデフォルトが = の場合、後続の単純なキャプチャは & で始まってはならず、 *this または this (C++20およびそれ以降) であっては (C++17およびそれ以降)なりません。

struct S2 { void f(int i); };
void S2::f(int i)
{
    [=]{};          // OK、コピーによるキャプチャデフォルト。
    [=, &i]{};      // OK、コピーによるキャプチャ、ただし i は参照によってキャプチャされます。
    [=, *this]{};   // C++17 まで: エラー、無効な構文。
                    // C++17 から: OK、囲っている S2 をコピーによってキャプチャします。
    [=, this] {};   // C++20 まで: エラー、 = がデフォルトのときの this。
                    // C++20 から: OK、 [=] と同じ。
}

いかなるキャプチャも一度だけ現れることができます。

struct S2 { void f(int i); };
void S2::f(int i)
{
    [i, i] {};        // エラー、 i が繰り返されています。
    [this, *this] {}; // エラー、「this」が繰り返されています。 (C++17)
}

ブロックスコープまたはデフォルトメンバ初期化子で定義されるラムダ式のみがキャプチャデフォルトや初期化子なしのキャプチャを持つことができます。 そのようなラムダ式の場合、最も内側の囲っている関数 (およびその引数) を含む、そこまでの囲っているスコープの集合として到達スコープが定義されます。 これはネストしたブロックスコープおよび囲っているラムダのスコープ (このラムダがネストされている場合) を含みます。

初期化子なしのあらゆるキャプチャにおける identifier (this キャプチャを除く) は、そのラムダの到達スコープで通常の無修飾名前探索を使用して探索されます。 探索の結果はその到達スコープ内で宣言された自動記憶域期間を持つ変数でなければなりません。 その変数 (または this) は明示的にキャプチャされます。

初期化子付きのキャプチャは、その宣言の範囲がそのラムダ式の本体である、 auto 型で宣言された変数を宣言し、それを明示的にキャプチャするかのように振る舞います。 ただし、

  • そのキャプチャがコピーによる場合、そのクロージャオブジェクトの非静的データメンバはその auto 変数を参照する別の方法になります。
  • そのキャプチャが参照による場合、その参照変数の生存期間はそのクロージャオブジェクトの生存期間が終了したときに終了します。

これは x = std::move(x) のようなキャプチャを使用してムーブオンリーな型をキャプチャするために使用できます。

これは &cr = std::as_const(x) のような方法で const 参照によってキャプチャすることも可能にします。

int x = 4;
auto y = [&r = x, x = x + 1]()->int
    {
        r += 2;
        return x * x;
    }(); // ::x を 6 に更新し、 y を 25 に初期化します。
(C++14およびそれ以降)

キャプチャリストにキャプチャデフォルトがあり、囲っているオブジェクト (this または *this) や自動変数を明示的にキャプチャしない場合、以下のいずれかであれば、それは暗黙にキャプチャされます。

  • ラムダの本体がその変数または this ポインタを ODR 使用する。
  • その変数または this ポインタが、総称ラムダ引数に依存する (C++17以前)式内の潜在的に評価される式内で使用される (非静的クラスメンバの使用の前に暗黙の this-> が追加されるときを含みます)。 この目的に対して、 typeid の非演算子は常に潜在的に評価されるとみなされます。 エンティティは、それが破棄される文内でのみ使用されている場合でも、暗黙にキャプチャされるかもしれません。 (C++17およびそれ以降)
void f(int, const int (&)[2] = {}) {} // #1
void f(const int&, const int (&)[1]) {} // #2
void test()
{
    const int x = 17;
    auto g0 = [](auto a) { f(x); }; // OK、 #1 を呼びます。 x をキャプチャしません。
    auto g1 = [=](auto a) { f(x); }; // C++14 では x をキャプチャしません。 C++17 では x をキャプチャします。
                                     // キャプチャは最適化によって除去されることがあります。
    auto g2 = [=](auto a) {
            int selector[sizeof(a) == 1 ? 1 : 2] = {};
            f(x, selector); // OK、 依存式であるため、 x をキャプチャします。
    };
    auto g3 = [=](auto a) {
      typeid(a + x);  // a + x が未評価被演算子であるかどうかにかかわらず x をキャプチャします。
    };
}
(C++14およびそれ以降)

ラムダの本体がコピーによってキャプチャされたエンティティを ODR 使用する場合、そのクロージャ型のメンバがアクセスされます。 それがエンティティの ODR 使用でない場合、アクセスは元のオブジェクトに行われます。

void f(const int*);
void g()
{
    const int N = 10;
    [=]{ 
        int arr[N]; // ODR 使用でない。 g の const int N を参照します。
        f(&N); // ODR 使用。 N を (コピーによって) キャプチャさせます。
               // &N は g のではなくクロージャオブジェクトのメンバ N のアドレスです。
    }();
}
ラムダが参照によってキャプチャされた参照を ODR 使用する場合、それはキャプチャされた参照自身ではなく、元の参照の参照先のオブジェクトを使用します。
#include <iostream>
 
auto make_function(int& x) {
  return [&]{ std::cout << x << '\n'; };
}
 
int main() {
  int i = 3;
  auto f = make_function(i); // f 内の x の使用は i に直接束縛します。
  i = 5;
  f(); // OK、 5 を表示します。
}


ラムダの本体内における、自動記憶域期間を持つ変数に対するあらゆる decltype の使用は、 decltype 自身が ODR 使用でなく実際のキャプチャが行われなくても、それがキャプチャされ ODR 使用されたかのように行われます。

void f3() {
    float x, &r = x;
    [=]
    { // x も r もキャプチャされません (decltype の被演算子内の出現は ODR 使用ではありません)
        decltype(x) y1; // y1 は float 型です。
        decltype((x)) y2 = y1; // このラムダが mutable でなく、 x が左辺値であるため、
                               // y2 は float const& 型です。
        decltype(r) r1 = y1;   // r1 は float& 型です (変換は考慮されません)。
        decltype((r)) r2 = y2; // r2 は float const& 型です。
    };
}

ラムダによって (明示的または暗黙に) キャプチャされたあらゆるエンティティは、そのラムダ式によって ODR 使用されます (そのため、ネストしたラムダによる暗黙のキャプチャは囲っているラムダにおける暗黙のキャプチャを誘発します)。

すべての暗黙にキャプチャされる変数は、そのラムダの到達スコープ内で宣言されていなければなりません。

ラムダが囲っているオブジェクトを (this または *this として) キャプチャする場合、最も近い囲っている関数が非静的メンバ関数であるか、そのラムダがデフォルトメンバ初期化子内にあるかの、いずれかでなければなりません。

struct s2 {
  double ohseven = .007;
  auto f() { // 以下の2つのラムダに対する最も近い囲っている関数。
    return [this] { // 囲っている s2 を参照によってキャプチャします。
      return [*this] { // 囲っている s2 をコピーによってキャプチャします (C++17)。
          return ohseven;// OK。
       }
     }();
  }
  auto g() {
     return []{ // 何もキャプチャしません。
         return [*this]{};// エラー、 *this が外側のラムダ式によってキャプチャされていません。
      }();
   }
};

ラムダ式 (または総称ラムダの関数呼び出し演算子の実体化) が this または自動記憶域期間を持ついずれかの変数を ODR 使用する場合、それはそのラムダ式によってキャプチャされなければなりません。

void f1(int i)
{
    int const N = 20;
    auto m1 = [=] {
            int const M = 30;
            auto m2 = [i] {
                    int x[N][M]; // N および M は ODR 使用されません
                                 // (キャプチャされていなくて OK です)。
                    x[0][0] = i; // i は m2 によって明示的にキャプチャされ、
                                 // m1 によって暗黙にキャプチャされます。
            };
    };
 
    struct s1 // f1() 内のローカルクラス
    {
        int f;
        void work(int n) // 非静的メンバ関数
        {
            int m = n * n;
            int j = 40;
            auto m3 = [this, m] {
                auto m4 = [&, j] { // エラー、 j は m3 によってキャプチャされていません。
                        int x = n; // エラー、 n は m4 によって暗黙にキャプチャされていますが
                                   // m3 によってキャプチャされていません。
                        x += m;    // OK、 m は m4 によって暗黙にキャプチャされ、
                                   // m3 によって明示的にキャプチャされています。
                        x += i;    // エラー、 i は到達スコープ (work() で終わる) の外側です。
                        x += f;    // OK、 this は m4 によって暗黙にキャプチャされ、
                                   // m3 によって明示的にキャプチャされています。
                };
            };
        }
    };
}

クラスのメンバは初期化子のないキャプチャによって明示的にキャプチャすることはできません (上で述べたように、キャプチャリスト内は変数のみが許されます)。

class S {
  int x = 0;
  void f() {
    int i = 0;
//  auto l1 = [i, x]{ use(i, x); };    // エラー、 x は変数ではありません。
    auto l2 = [i, x=x]{ use(i, x); };  // OK、コピーキャプチャ。
    i = 1; x = 1; l2();                // use(0, 0) を呼びます。
    auto l3 = [i, &x=x]{ use(i, x); }; // OK、参照キャプチャ。
    i = 2; x = 2; l3();                // use(1, 2) を呼びます。
  }
};

ラムダがコピーによる暗黙のキャプチャを用いてメンバをキャプチャするとき、それはそのメンバ変数のコピーを作成しません。 メンバ変数 m の使用は式 (*this).m として扱われ、 *this は常に参照によって暗黙にキャプチャされます。

class S {
  int x = 0;
  void f() {
    int i = 0;
    auto l1 = [=]{ use(i, x); }; // i のコピーと this ポインタのコピーをキャプチャします。
    i = 1; x = 1; l1(); // コピーによる i および参照による x であるかのように、 use(0, 1) を呼びます。
    auto l2 = [i, this]{ use(i, x); }; // 同上ですが、明示的に行います。
    i = 2; x = 2; l2(); // コピーによる i および参照による x であるかのように、 use(1, 2) を呼びます。
    auto l3 = [&]{ use(i, x); }; // 参照による i および this ポインタのコピーをキャプチャします。
    i = 3; x = 2; l3(); // どちらも参照による i および x であるかのように、 use(3, 2) を呼びます。
    auto l4 = [i, *this]{ use(i, x); }; // *this のコピー (x のコピーを含む) を作成します。
    i = 4; x = 4; l4(); // どちらもコピーによる i および x であるかのように、 use(3, 2) を呼びます。
  }
};

ラムダ式がデフォルト引数内に現れる場合は、明示的にも暗黙的にも何もキャプチャできません。

無名共用体のメンバはキャプチャできません。

ネストしたラムダ m2 が直接囲っているラムダ m1 によってもキャプチャされている何かをキャプチャする場合、 m2 のキャプチャは以下のように変換されます。

  • 囲っているラムダ m1 がコピーによってキャプチャしている場合、 m2 は元の変数や this ではなく、 m1 のクロージャ型の非静的メンバをキャプチャします。
  • 囲っているラムダ m1 が参照によってキャプチャしている場合、 m2 は元の変数または this をキャプチャします。
#include <iostream>
 
int main()
{
    int a = 1, b = 1, c = 1;
 
    auto m1 = [a, &b, &c]() mutable {
        auto m2 = [a, b, &c]() mutable {
            std::cout << a << b << c << '\n';
            a = 4; b = 4; c = 4;
        };
        a = 3; b = 3; c = 3;
        m2();
    };
 
    a = 2; b = 2; c = 2;
 
    m1();                             // m2() を呼び、 123 を表示します。
    std::cout << a << b << c << '\n'; // 234 を表示します。
}


[編集]

この例は、ラムダを汎用アルゴリズムに渡す方法、およびラムダ宣言の結果のオブジェクトをどのように std::function オブジェクトに格納できるかを示します。

#include <vector>
#include <iostream>
#include <algorithm>
#include <functional>
 
int main()
{
    std::vector<int> c = {1, 2, 3, 4, 5, 6, 7};
    int x = 5;
    c.erase(std::remove_if(c.begin(), c.end(), [x](int n) { return n < x; }), c.end());
 
    std::cout << "c: ";
    std::for_each(c.begin(), c.end(), [](int i){ std::cout << i << ' '; });
    std::cout << '\n';
 
    // the type of a closure cannot be named, but can be inferred with auto
    // since C++14, lambda could own default arguments
    auto func1 = [](int i = 6) { return i + 4; };
    std::cout << "func1: " << func1() << '\n';
 
    // like all callable objects, closures can be captured in std::function
    // (this may incur unnecessary overhead)
    std::function<int(int)> func2 = [](int i) { return i + 4; };
    std::cout << "func2: " << func2(6) << '\n';
}

出力:

c: 5 6 7
func1: 10
func2: 10

[編集] 欠陥報告

以下の動作変更欠陥報告は以前に発行された C++ 標準に遡って適用されました。

DR 適用先 発行時の動作 正しい動作
CWG 975 C++11 the return type of closure's operator() was only deduced if lambda body contains a single return deduced as if for C++14 auto-returning function
CWG 1891 C++14 closure had a deleted default ctor and implicit copy/move ctors no default and defaulted copy/move
CWG 1722 C++14 the conversion function for captureless lambdas had unspecified exception specification conversion function is noexcept

[編集] 関連項目

auto 指定子 式によって定義される型を指定します (C++11) [edit]
(C++11)
指定された関数呼び出しシグネチャを持つ任意の型の呼び出し可能なオブジェクトをラップします
(クラステンプレート) [edit]