名前空間
変種
操作

ラムダ式 (C++11 以降)

From cppreference.com
< cpp‎ | language
 
 
C++言語
全般
フロー制御
条件実行文
if
繰り返し文 (ループ)
for
範囲for (C++11)
ジャンプ文
関数
関数宣言
ラムダ式
inline指定子
動的例外仕様 (C++17まで*)
noexcept指定子 (C++11)
例外
名前空間
指定子
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
記憶域期間指定子
初期化
代替表現
リテラル
ブーリアン - 整数 - 浮動小数点数
文字 - 文字列 - nullptr (C++11)
ユーザー定義 (C++11)
ユーティリティ
属性 (C++11)
typedef宣言
型エイリアス宣言 (C++11)
キャスト
メモリ確保
クラス
クラス固有の関数プロパティ
explicit (C++11)
static

特殊メンバ関数
テンプレート
その他
 
 
 

クロージャ (スコープ内の変数をキャプチャできる名前のない関数オブジェクト) を構築します。

目次

[編集] 構文

[編集] 明示的なテンプレートパラメータリストを持たないラムダ式 (非ジェネリックの可能性あり)
[captures ] front-attr (optional) (params ) specs (optional) except (optional)
back-attr (optional) trailing (optional) requires (optional) contract-specs (optional) { body }
(1)
[captures ] { body } (2) (C++23まで)
[captures ] front-attr (optional) trailing (optional) contract-specs (optional) { body } (2) (C++23から)
[captures ] front-attr (optional) except
back-attr (optional) trailing (optional) contract-specs (optional) { body }
(3) (C++23から)
[captures ] front-attr (optional) specs except (optional)
back-attr (optional) trailing (optional) contract-specs (optional) { body }
(4) (C++23から)
[編集] 明示的なテンプレートパラメータリストを持つラムダ式 (常にジェネリック) (C++20 以降)
[captures ] <tparams > t-requires (optional)
front-attr (optional) (params ) specs (optional) except (optional)
back-attr (optional) trailing (optional) requires (optional) contract-specs (optional) { body }
(1)
[captures ] <tparams > t-requires (optional) { body } (2) (C++23まで)
[captures ] <tparams > t-requires (optional)
front-attr (optional) trailing (optional) contract-specs (optional) { body }
(2) (C++23から)
[captures ] <tparams > t-requires (optional) front-attr (optional) except
back-attr (optional) trailing (optional) contract-specs (optional) { body }
(3) (C++23から)
[captures ] <tparams > t-requires (optional) front-attr (optional) specs except (optional)
back-attr (optional) trailing (optional) contract-specs (optional) { body }
(4) (C++23から)
1) パラメータリストを持つラムダ式。
2-4) パラメータリストを持たないラムダ式。
2) 最も単純な構文。back-attrは適用できません。
3,4) back-attrは、specsおよびexceptのいずれかが存在する場合にのみ適用できます。

[編集] 説明

captures - キャプチャするエンティティを指定します。
tparams - 空ではないカンマ区切りのテンプレートパラメータリスト。ジェネリックラムダのテンプレートパラメータに名前を付けるために使用されます (以下のClosureType::operator()を参照)。
t-requires - tparams制約を追加します。

t-requiresが属性指定子シーケンスで終わる場合、シーケンス内の属性はfront-attrの属性として扱われます。

(C++23から)
front-attr - (C++23 以降) 属性指定子シーケンスは、クロージャ型のoperator()に適用されます (したがって、[[noreturn]]属性を使用できます)。
params - クロージャ型のoperator()パラメータリスト

明示的なオブジェクトパラメータを持つことができます。

(C++23から)
specs - 次の指定子のリスト。各指定子は、各シーケンスで最大1回まで許可されます。
指定子 効果
mutable bodyがコピーでキャプチャされたオブジェクトを変更し、非constメンバ関数を呼び出すことを許可します。
  • 明示的なオブジェクトパラメータが存在する場合は使用できません。
(C++23から)
constexpr     
(C++17以降)
operator()constexpr関数であることを明示的に指定します。
  • operator()がすべてのconstexpr関数の要件を満たしていれば、constexprが指定されていなくてもoperator()はconstexprになります。
consteval
(C++20以降)
operator()即時関数であることを指定します。
  • constevalconstexprは同時に指定できません。
static
(C++23から)
operator()静的メンバ関数であることを指定します。
  • staticmutableは同時に指定できません。
  • capturesが空ではない場合、または明示的なオブジェクトパラメータが存在する場合は使用できません。
except - クロージャ型のoperator()に対する動的例外指定または(C++20 まで)noexcept指定子を提供します。
back-attr - 属性指定子シーケンスは、クロージャ型のoperator()の型に適用されます (したがって、[[noreturn]]属性は使用できません)。
trailing - -> ret。ここでretは戻り値の型を指定します。
要求するようになった - (C++20 以降) クロージャ型のoperator()制約を追加します。
contract-specs - (C++26 以降) クロージャ型のoperator()に対する関数契約指定子のリスト。
body - 関数本体。


パラメータの型としてautoが使用される場合、または明示的なテンプレートパラメータリストが提供される場合(C++20 以降)、ラムダは「ジェネリックラムダ」です。

(C++14以降)

bodyの先頭では、__func__変数が暗黙的に定義され、そのセマンティクスはここで説明されています。

[編集] クロージャ型

ラムダ式は、**クロージャ型**として知られる、一意な名前のない非共用体集約体クラス型のprvalue式であり、(ADLの目的で) ラムダ式を含む最小のブロックスコープ、クラススコープ、または名前空間スコープで宣言されます。

クロージャ型は、capturesが空である場合にのみ、構造型です。

(C++20以降)

クロージャ型は次のメンバを持ち、それらは明示的にインスタンス化明示的に特殊化、または(C++14 以降)フレンド宣言で名前を付けることはできません。

ClosureType::operator()(params)

ret operator()(params) { body }
(static および const が存在する場合あり、以下を参照)
template<template-params>
ret operator()(params) { body }
(C++14以降)
(ジェネリックラムダ、static および const が存在する場合あり、以下を参照)

呼び出されたときにラムダ式の本体を実行します。変数を参照するときは、キャプチャされたコピー (コピーでキャプチャされたエンティティの場合) または元のオブジェクト (参照でキャプチャされたエンティティの場合) を参照します。

operator()のパラメータリストは、提供されていればparamsであり、そうでなければ空のパラメータリストです。

operator()の戻り値の型は、trailing で指定された型です。

trailingが指定されていない場合、operator()の戻り値の型は自動的に推論されます。[1]

ラムダ指定子でキーワードmutableが使用されていない場合、または明示的なオブジェクトパラメータが存在する場合を除き(C++23 以降)operator()のcv修飾子はconstであり、コピーでキャプチャされたオブジェクトは、このoperator()内から変更できません。明示的なconst修飾子は許可されません。operator()は決して仮想ではなく、volatile修飾子を持つことはできません。

operator()は、constexpr関数の要件を満たす場合、常にconstexprです。ラムダ指定子でキーワードconstexprが使用された場合もconstexprです。

(C++17以降)

operator()は、ラムダ指定子でキーワードconstevalが使用された場合、即時関数です。

(C++20以降)

operator()は、ラムダ指定子でキーワードstaticが使用された場合、静的メンバ関数です。

paramsに明示的なオブジェクトパラメータが含まれている場合、operator()明示的オブジェクトメンバ関数です。

(C++23から)


params内の型がautoと指定されている各パラメータについて、出現順に架空のテンプレートパラメータがtemplate-paramsに追加されます。対応するparamsの関数メンバが関数パラメータパックである場合、架空のテンプレートパラメータはパラメータパックである可能性があります。

// generic lambda, operator() is a template with two parameters
auto glambda = [](auto a, auto&& b) { return a < b; };
bool b = glambda(3, 3.14); // OK
 
// generic lambda, operator() is a template with one parameter
auto vglambda = [](auto printer)
{
    return [=](auto&&... ts) // generic lambda, ts is a parameter pack
    { 
        printer(std::forward<decltype(ts)>(ts)...);
        // nullary lambda (takes no parameters):
        return [=] { printer(ts...); };
    };
};
 
auto p = vglambda([](auto v1, auto v2, auto v3)
{
    std::cout << v1 << v2 << v3;
});
 
auto q = p(1, 'a', 3.14); // outputs 1a3.14
q();                      // outputs 1a3.14
(C++14以降)


ラムダ定義が明示的なテンプレートパラメータリストを使用する場合、そのテンプレートパラメータリストがoperator()で使用されます。params内の型がautoと指定されている各パラメータについて、追加の架空のテンプレートパラメータがそのテンプレートパラメータリストの末尾に追加されます。

// generic lambda, operator() is a template with two parameters
auto glambda = []<class T>(T a, auto&& b) { return a < b; };
 
// generic lambda, operator() is a template with one parameter pack
auto f = []<typename... Ts>(Ts&&... ts)
{
    return foo(std::forward<Ts>(ts)...);
};
(C++20以降)

ラムダ式上の例外指定exceptは、operator()に適用されます。

名前探索thisポインタの型と値の決定、および非静的クラスメンバへのアクセスの目的のために、クロージャ型のoperator()の本体は、ラムダ式のコンテキストで考慮されます。

struct X
{
    int x, y;
    int operator()(int);
    void f()
    {
        // the context of the following lambda is the member function X::f
        [=]() -> int
        {
            return operator()(this->x + y); // X::operator()(this->x + (*this).y)
                                            // this has type X*
        };
    }
};

宙に浮いた参照

非参照エンティティが参照によって暗黙的または明示的にキャプチャされ、そのエンティティの寿命が終了した後にクロージャオブジェクトのoperator()が呼び出されると、未定義の動作が発生します。C++のクロージャは、参照によってキャプチャされたオブジェクトの寿命を延長しません。

現在の*thisオブジェクトがthisを介してキャプチャされた場合も同様に、そのオブジェクトの寿命に適用されます。

  1. 関数戻り値型推論は C++14 で導入されましたが、そのルールは C++11 のラムダ戻り値型推論で利用可能です。

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

キャプチャなし非ジェネリックラムダ
using F = ret(*)(params);
operator F() const noexcept;
(C++17まで)
using F = ret(*)(params);
constexpr operator F() const noexcept;
(C++17以降)
キャプチャなしジェネリックラムダ
template<template-params> using fptr_t = /* see below */;

template<template-params>

operator fptr_t<template-params>() const noexcept;
(C++14以降)
(C++17まで)
template<template-params> using fptr_t = /* see below */;

template<template-params>

constexpr operator fptr_t<template-params>() const noexcept;
(C++17以降)

このユーザー定義変換関数は、ラムダ式にcaptures がなく、かつ明示的なオブジェクトパラメータがない場合のみ定義されます(C++23 以降)。これは、クロージャオブジェクトのpublic、constexpr、(C++17 以降)非仮想、非explicit、const noexceptメンバ関数です。

この関数は、関数呼び出し演算子 (またはジェネリックラムダの場合は特殊化) が即時関数である場合、即時関数です。

(C++20以降)

ジェネリックなキャプチャなしラムダは、operator()と同じ架空のテンプレートパラメータリストを持つユーザー定義変換関数テンプレートを持ちます。

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); // error: not convertible
h(glambda);  // OK: calls #1 since #2 is not convertible
 
int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; // OK
(C++14以降)


変換関数によって返される値は、呼び出されたときに、クロージャ型のデフォルト構築されたインスタンスに対してクロージャ型の関数呼び出し演算子を呼び出すのと同じ効果を持つ、C++の言語リンケージを持つ関数へのポインタです。

(C++14まで)

変換関数 (テンプレート) によって返される値は、呼び出されたときに、C++の言語リンケージを持つ関数へのポインタで、その効果は以下の通りです。

  • 非ジェネリックラムダの場合、クロージャ型のデフォルト構築されたインスタンスに対してクロージャ型のoperator()を呼び出すのと同じ効果を持ちます。
  • ジェネリックラムダの場合、クロージャ型のデフォルト構築されたインスタンスに対してジェネリックラムダの対応するoperator()特殊化を呼び出すのと同じ効果を持ちます。
(C++14以降)
(C++23まで)

変換関数 (テンプレート) によって返される値は、

  • operator()が静的である場合、C++の言語リンケージを持つそのoperator()へのポインタ。
  • それ以外の場合、呼び出されたときに、C++の言語リンケージを持つ関数へのポインタで、その効果は以下の通りです。
    • 非ジェネリックラムダの場合、クロージャ型のデフォルト構築されたインスタンスに対してクロージャ型のoperator()を呼び出すのと同じ効果を持ちます。
    • ジェネリックラムダの場合、クロージャ型のデフォルト構築されたインスタンスに対してジェネリックラムダの対応するoperator()特殊化を呼び出すのと同じ効果を持ちます。
(C++23から)


この関数は、関数呼び出し演算子 (またはジェネリックラムダの場合は特殊化) が 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); // error: no specialization can be
                                // constexpr because of static s

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

(C++17以降)

ClosureType::ClosureType()

ClosureType() = default;
(C++20以降)
(キャプチャが指定されていない場合のみ)
ClosureType(const ClosureType&) = default;
ClosureType(ClosureType&&) = default;

クロージャ型はDefaultConstructibleではありません。クロージャ型にはデフォルトコンストラクタがありません。

(C++20まで)

capturesが指定されていない場合、クロージャ型にはデフォルト化されたデフォルトコンストラクタがあります。それ以外の場合、デフォルトコンストラクタはありません (これは、capture-default があっても、実際に何もキャプチャしない場合を含みます)。

(C++20以降)

コピーコンストラクタとムーブコンストラクタは、デフォルト化されたものとして宣言され、コピーコンストラクタおよびムーブコンストラクタの通常のルールに従って暗黙的に定義されることがあります。

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以降)
(それ以外の場合)

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

(C++20まで)

capturesが指定されていない場合、クロージャ型はデフォルト化されたコピー代入演算子とデフォルト化されたムーブ代入演算子を持ちます。それ以外の場合、deletedコピー代入演算子を持ちます (これは、capture-default があっても、実際に何もキャプチャしない場合を含みます)。

(C++20以降)

ClosureType::~ClosureType()

~ClosureType() = default;

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

ClosureType::Captures

T1 a;

T2 b;

...

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

初期化子を持たないキャプチャに対応するデータメンバは、ラムダ式が評価されるときに直接初期化されます。初期化子を持つキャプチャに対応するデータメンバは、初期化子が必要とするように初期化されます (コピー初期化または直接初期化のいずれか)。配列がキャプチャされた場合、配列要素はインデックスの昇順で直接初期化されます。データメンバが初期化される順序は、それらが宣言された順序です (これは指定されていません)。

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

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


ラムダ式は、未評価の式テンプレート引数エイリアス宣言typedef宣言、および関数本体と関数のデフォルト引数を除く、関数 (または関数テンプレート) 宣言のどこにも許可されません。

(C++20まで)

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

capturesは、ラムダ関数本体内からアクセス可能な外部変数を定義します。その構文は次のように定義されます。

capture-default (1)
capture-list (2)
capture-default , capture-list (3)
capture-default - & または = のいずれか
capture-list - カンマ区切りのcapture のリスト


captureの構文は次のように定義されます。

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

capture-default&の場合、後続の単純なキャプチャは&で始まってはなりません。

struct S2 { void f(int i); };
void S2::f(int i)
{
    [&] {};          // OK: by-reference capture default
    [&, i] {};       // OK: by-reference capture, except i is captured by copy
    [&, &i] {};      // Error: by-reference capture when by-reference is the default
    [&, this] {};    // OK, equivalent to [&]
    [&, this, i] {}; // OK, equivalent to [&, i]
}

capture-default=の場合、後続の単純なキャプチャは&で始まらなければなりません、または*thisでなければなりません(C++17 以降) またはthisでなければなりません(C++20 以降)

struct S2 { void f(int i); };
void S2::f(int i)
{
    [=] {};        // OK: by-copy capture default
    [=, &i] {};    // OK: by-copy capture, except i is captured by reference
    [=, *this] {}; // until C++17: Error: invalid syntax
                   // since C++17: OK: captures the enclosing S2 by copy
    [=, this] {};  // until C++20: Error: this when = is the default
                   // since C++20: OK, same as [=]
}

どのキャプチャも1回しか出現できず、その名前はどのパラメータ名とも異なっている必要があります。

struct S2 { void f(int i); };
void S2::f(int i)
{
    [i, i] {};        // Error: i repeated
    [this, *this] {}; // Error: "this" repeated (C++17)
 
    [i] (int i) {};   // Error: parameter and capture have the same name
}

ラムダ式は、変数をキャプチャせずに使用できます。その変数が

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

ラムダ式は、変数をキャプチャせずにその値を読み取ることができます。その変数が

  • const非volatile整数型または列挙型であり、定数式で初期化されている場合、または
  • constexprであり、mutableメンバを持たない場合。

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

次の条件のいずれかを満たすラムダ式のみが、初期化子なしのcapture-defaultまたはcaptureを持つことができます。

(C++26以降)

そのようなラムダ式の場合、*到達スコープ*は、最も内側の囲む関数 (およびそのパラメータ) を含む、囲むスコープのセットとして定義されます。これには、このラムダがネストされている場合のネストされたブロックスコープと囲むラムダのスコープが含まれます。

初期化子を持たないキャプチャ (thisキャプチャを除く) のidentifierは、ラムダの*到達スコープ*内で通常の非修飾名前探索を使用して探索されます。探索の結果は、到達スコープで宣言された自動記憶域期間を持つ変数でなければなりません、または対応する変数がそのような要件を満たす構造化束縛でなければなりません(C++20 以降)。エンティティは*明示的にキャプチャ*されます。

*初期化キャプチャ*と呼ばれる初期化子を持つキャプチャは、型指定子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;
}(); // updates ::x to 6 and initializes y to 25.
(C++14以降)

capturescapture-defaultを持ち、囲むオブジェクト (thisまたは*thisとして)、またはラムダ本体でodr-usableな自動変数、または対応する変数が自動記憶域期間を持つ構造化束縛(C++20 以降)を明示的にキャプチャしない場合、そのエンティティが式内の潜在的に評価される式内で名前が付けられている場合 (非静的クラスメンバの使用前に暗黙的なthis->が追加される場合を含む)、エンティティを*暗黙的にキャプチャ*します。

暗黙的なキャプチャを決定する目的で、typeidは決してそのオペランドを未評価にすると見なされません。

エンティティは、ラムダ本体のインスタンス化後に破棄された文内で名前が付けられているだけでも、暗黙的にキャプチャされる可能性があります。

(C++17以降)
void f(int, const int (&)[2] = {}) {}   // #1
void f(const int&, const int (&)[1]) {} // #2
 
struct NoncopyableLiteralType
{
    constexpr explicit NoncopyableLiteralType(int n) : n_(n) {}
    NoncopyableLiteralType(const NoncopyableLiteralType&) = delete;
 
    int n_;
};
 
void test()
{
    const int x = 17;
 
    auto l0 = []{ f(x); };           // OK: calls #1, does not capture x
    auto g0 = [](auto a) { f(x); };  // same as above
 
    auto l1 = [=]{ f(x); };          // OK: captures x (since P0588R1) and calls #1
                                     // the capture can be optimized away
    auto g1 = [=](auto a) { f(x); }; // same as above
 
    auto ltid = [=]{ typeid(x); };   // OK: captures x (since P0588R1)
                                     // even though x is unevaluated
                                     // the capture can be optimized away
 
    auto g2 = [=](auto a)
    {
        int selector[sizeof(a) == 1 ? 1 : 2] = {};
        f(x, selector); // OK: is a dependent expression, so captures x
    };
 
    auto g3 = [=](auto a)
    {
        typeid(a + x);  // captures x regardless of
                        // whether a + x is an unevaluated operand
    };
 
    constexpr NoncopyableLiteralType w{42};
    auto l4 = []{ return w.n_; };      // OK: w is not odr-used, capture is unnecessary
    // auto l5 = [=]{ return w.n_; };  // error: w needs to be captured by copy
}

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

void f(const int*);
void g()
{
    const int N = 10;
    [=]
    { 
        int arr[N]; // not an odr-use: refers to g's const int N
        f(&N); // odr-use: causes N to be captured (by copy)
               // &N is the address of the closure object's member N, not g's N
    }();
}

参照によってキャプチャされた参照をラムダがodr-useする場合、それはキャプチャされた参照自体ではなく、元の参照が指すオブジェクトを使用しています。

#include <iostream>
 
auto make_function(int& x)
{
    return [&] { std::cout << x << '\n'; };
}
 
int main()
{
    int i = 3;
    auto f = make_function(i); // the use of x in f binds directly to i
    i = 5;
    f(); // OK: prints 5
}

キャプチャデフォルトが=のラムダの本体内では、キャプチャ可能なエンティティの型は、(ラムダがmutableでない場合、const修飾がよく追加されるため) キャプチャされているかのように扱われます。たとえそのエンティティが未評価のオペランドにあり、キャプチャされていない場合でも (例: decltype内)。

void f3()
{
    float x, &r = x;
    [=]
    { // x and r are not captured (appearance in a decltype operand is not an odr-use)
        decltype(x) y1;        // y1 has type float
        decltype((x)) y2 = y1; // y2 has type float const& because this lambda
                               // is not mutable and x is an lvalue
        decltype(r) r1 = y1;   // r1 has type float& (transformation not considered)
        decltype((r)) r2 = y2; // r2 has type float const&
    };
}

ラムダによって (暗黙的または明示的に) キャプチャされたすべてのエンティティは、ラムダ式によってodr-useされます (したがって、ネストされたラムダによる暗黙的なキャプチャは、囲むラムダでの暗黙的なキャプチャをトリガーします)。

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

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

struct s2
{
    double ohseven = .007;
    auto f() // nearest enclosing function for the following two lambdas
    {
        return [this]      // capture the enclosing s2 by reference
        {
            return [*this] // capture the enclosing s2 by copy (C++17)
            {
                return ohseven;// OK
            }
        }();
    }
 
    auto g()
    {
        return [] // capture nothing
        { 
            return [*this] {};// error: *this not captured by outer lambda expression
        }();
    }
};

ラムダ式 (またはジェネリックラムダの関数呼び出し演算子の特殊化)(C++14 以降)*thisまたは自動記憶域期間を持つ変数をODR-useする場合、それはラムダ式によってキャプチャされなければなりません。

void f1(int i)
{
    int const N = 20;
    auto m1 = [=]
    {
        int const M = 30;
        auto m2 = [i]
        {
            int x[N][M]; // N and M are not odr-used 
                         // (ok that they are not captured)
            x[0][0] = i; // i is explicitly captured by m2
                         // and implicitly captured by m1
        };
    };
 
    struct s1 // local class within f1()
    {
        int f;
        void work(int n) // non-static member function
        {
            int m = n * n;
            int j = 40;
            auto m3 = [this, m]
            {
                auto m4 = [&, j] // error: j is not captured by m3
                {
                    int x = n; // error: n is implicitly captured by m4
                               // but not captured by m3
                    x += m;    // OK: m is implicitly captured by m4
                               // and explicitly captured by m3
                    x += i;    // error: i is outside of the reaching scope
                               // (which ends at work())
                    x += f;    // OK: this is captured implicitly by m4
                               // and explicitly captured by m3
                };
            };
        }
    };
}

クラスメンバは、初期化子なしのキャプチャによって明示的にキャプチャすることはできません (上記のように、capture-list では変数のみが許可されます)。

class S
{
    int x = 0;
 
    void f()
    {
        int i = 0;
    //  auto l1 = [i, x] { use(i, x); };      // error: x is not a variable
        auto l2 = [i, x = x] { use(i, x); };  // OK, copy capture
        i = 1; x = 1; l2(); // calls use(0,0)
        auto l3 = [i, &x = x] { use(i, x); }; // OK, reference capture
        i = 2; x = 2; l3(); // calls use(1,2)
    }
};

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

class S
{
    int x = 0;
 
    void f()
    {
        int i = 0;
 
        auto l1 = [=] { use(i, x); }; // captures a copy of i and
                                      // a copy of the this pointer
        i = 1; x = 1; l1();           // calls use(0, 1), as if
                                      // i by copy and x by reference
 
        auto l2 = [i, this] { use(i, x); }; // same as above, made explicit
        i = 2; x = 2; l2();           // calls use(1, 2), as if
                                      // i by copy and x by reference
 
        auto l3 = [&] { use(i, x); }; // captures i by reference and
                                      // a copy of the this pointer
        i = 3; x = 2; l3();           // calls use(3, 2), as if
                                      // i and x are both by reference
 
        auto l4 = [i, *this] { use(i, x); }; // makes a copy of *this,
                                             // including a copy of x
        i = 4; x = 4; l4();           // calls use(3, 2), as if
                                      // i and x are both by copy
    }
};

ラムダ式がデフォルト引数内に出現する場合、明示的または暗黙的に何もキャプチャすることはできません。ただし、すべてのキャプチャがデフォルト引数内に出現する式の制約を満たす初期化子を持つ場合は除きます(C++14 以降)

void f2()
{
    int i = 1;
 
    void g1( int = [i] { return i; }() ); // error: captures something
    void g2( int = [i] { return 0; }() ); // error: captures something
    void g3( int = [=] { return i; }() ); // error: captures something
 
    void g4( int = [=] { return 0; }() );       // OK: capture-less
    void g5( int = [] { return sizeof i; }() ); // OK: capture-less
 
    // C++14
    void g6( int = [x = 1] { return x; }() ); // OK: 1 can appear
                                              //     in a default argument
    void g7( int = [x = i] { return x; }() ); // error: i cannot appear
                                              //        in a default argument
}

匿名共用体のメンバはキャプチャできません。ビットフィールドはコピーでのみキャプチャできます。

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

  • 囲むラムダm1がコピーによってキャプチャする場合、m2m1のクロージャ型の非静的メンバをキャプチャし、元の変数または*thisではありません。m1がmutableでない場合、非静的データメンバはconst修飾されていると見なされます。
  • 囲むラムダ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();                             // calls m2() and prints 123
    std::cout << a << b << c << '\n'; // prints 234
}

ラムダが何かをキャプチャする場合、関数呼び出し演算子の明示的なオブジェクトパラメータ (もしあれば) の型は、次のいずれかである必要があります。

  • クロージャ型、
  • クロージャ型からpublicかつ曖昧さなく派生したクラス型、または
  • cv修飾されている可能性のあるそのような型への参照。
struct C 
{
    template<typename T>
    C(T);
};
 
void func(int i) 
{
    int x = [=](this auto&&) { return i; }();  // OK
    int y = [=](this C) { return i; }();       // error
    int z = [](this C) { return 42; }();       // OK
 
    auto lambda = [n = 42] (this auto self) { return n; };
    using Closure = decltype(lambda);
    struct D : private Closure {
        D(Closure l) : Closure(l) {}
        using Closure::operator();
        friend Closure;
    };
    D{lambda}(); // error
}
(C++23から)

[編集] 注釈

機能テストマクロ 規格 機能
__cpp_lambdas 200907L (C++11) ラムダ式
__cpp_generic_lambdas 201304L (C++14) ジェネリックラムダ式
201707L (C++20) ジェネリックラムダのための明示的なテンプレートパラメータリスト
__cpp_init_captures 201304L (C++14) ラムダ初期化キャプチャ
201803L (C++20) ラムダ初期化キャプチャでのパック展開を許可
__cpp_capture_star_this 201603L (C++17) *thisの値キャプチャを[=, *this]として許可
__cpp_constexpr 201603L (C++17) constexprラムダ
__cpp_static_call_operator 202207L (C++23) キャプチャなしラムダのための静的operator()

暗黙的なラムダキャプチャのルールは、Defect Report P0588R1によってわずかに変更されました。2023年10月現在、いくつかの主要な実装ではまだDRが完全に実装されておらず、odr-useを検出する古いルールがまだ一部のケースで使用されています。

P0588R1以前の古いルール

capturescapture-defaultを持ち、囲むオブジェクト (thisまたは*thisとして)、またはラムダ本体でodr-usableな自動変数、または対応する変数が自動記憶域期間を持つ構造化束縛(C++20 以降)を明示的にキャプチャしない場合、そのエンティティが次のいずれかに該当するときに*暗黙的にキャプチャ*します。

  • ジェネリックラムダのテンプレートパラメータに依存する式内の潜在的に評価される式で名前が付けられている、または
(C++14以降)
  • ラムダの本体によってodr-useされている。

[編集]

この例では、ラムダをジェネリックアルゴリズムに渡す方法と、ラムダ式から生じるオブジェクトをstd::functionオブジェクトに格納する方法を示します。

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
 
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';
 
    constexpr int fib_max {8};
    std::cout << "Emulate `recursive lambda` calls:\nFibonacci numbers: ";
    auto nth_fibonacci = [](int n)
    {
        std::function<int(int, int, int)> fib = [&](int n, int a, int b)
        {
            return n ? fib(n - 1, a + b, a) : b;
        };
        return fib(n, 0, 1);
    };
 
    for (int i{1}; i <= fib_max; ++i)
        std::cout << nth_fibonacci(i) << (i < fib_max ? ", " : "\n");
 
    std::cout << "Alternative approach to lambda recursion:\nFibonacci numbers: ";
    auto nth_fibonacci2 = [](auto self, int n, int a = 0, int b = 1) -> int
    {
        return n ? self(self, n - 1, a + b, a) : b;
    };
 
    for (int i{1}; i <= fib_max; ++i)
        std::cout << nth_fibonacci2(nth_fibonacci2, i) << (i < fib_max ? ", " : "\n");
 
#ifdef __cpp_explicit_this_parameter
    std::cout << "C++23 approach to lambda recursion:\n";
    auto nth_fibonacci3 = [](this auto self, int n, int a = 0, int b = 1) -> int
    {
         return n ? self(n - 1, a + b, a) : b;
    };
 
    for (int i{1}; i <= fib_max; ++i)
        std::cout << nth_fibonacci3(i) << (i < fib_max ? ", " : "\n");
#endif
}

実行結果の例

c: 5 6 7
func1: 10
func2: 10
Emulate `recursive lambda` calls:
Fibonacci numbers: 0, 1, 1, 2, 3, 5, 8, 13
Alternative approach to lambda recursion:
Fibonacci numbers: 0, 1, 1, 2, 3, 5, 8, 13

[編集] Defect reports

以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。

DR 適用対象 公開された動作 正しい動作
CWG 974 C++11 デフォルト引数はラムダ式のパラメータリストでは許可されていなかった
ラムダ式のパラメータリスト
許可
CWG 1048
(N3638)
C++11 戻り値の型は、1つのreturn文のみを含むラムダ本体に対してのみ推論できた
ラムダ本体
戻り値の型推論を改善した
型推論
CWG 1249 C++11 囲む非mutableラムダのキャプチャされたメンバがconstと見なされるかどうか不明確であった
と見なされるかどうか
constと見なされる
CWG 1557 C++11 クロージャ型の変換関数の返される関数型の言語リンケージが指定されていなかった
変換関数
C++の言語リンケージを持つ
言語リンケージ
CWG 1607 C++11 ラムダ式が関数および関数テンプレートのシグネチャに出現できた
関数テンプレートのシグネチャ
許可されなくなった。
CWG 1612 C++11 匿名共用体のメンバがキャプチャできた 許可されなくなった。
CWG 1722 C++11 キャプチャなしラムダの変換関数の例外指定が指定されていなかった
例外指定
変換関数はnoexceptである
noexcept
CWG 1772 C++11 ラムダ本体内の__func__のセマンティクスが不明確であった クロージャクラスのoperator()を参照する
class's operator()
CWG 1780 C++14 ジェネリックラムダのクロージャ型のメンバが明示的にインスタンス化または特殊化できるか不明確であった
インスタンス化または特殊化
どちらも許可されない
CWG 1891 C++11 クロージャは deleted デフォルトコンストラクタと暗黙的なコピー/ムーブコンストラクタを持っていた
コピー/ムーブコンストラクタ
デフォルトなし、デフォルト化されたコピー/ムーブコンストラクタ
コピー/ムーブコンストラクタ
CWG 1937 C++11 変換関数の結果を呼び出す効果については、どのオブジェクトに対してoperator()を呼び出すと
変換関数
同じ効果を持つか指定されていなかった
クロージャ型のデフォルト構築されたインスタンスに対して
インスタンス
CWG 1973 C++11 クロージャ型のoperator()のパラメータリストは、trailingで与えられたパラメータリストを参照できる
trailing
paramsのみを参照できる
params
CWG 2011 C++11 参照によってキャプチャされた参照について、キャプチャの識別子がどのエンティティを参照するかは指定されていなかった
参照
元の参照エンティティを参照する
参照エンティティ
CWG 2095 C++11 関数へのrvalue参照をコピーでキャプチャする動作が不明確であった
不明確であった
明確化された
CWG 2211 C++11 キャプチャがパラメータと同じ名前を持つ場合の動作は指定されていなかった
パラメータ
この場合、プログラムは
不正となる
CWG 2358 C++14 デフォルト引数に出現するラムダ式は、すべてのキャプチャがデフォルト引数に出現できる式で初期化されている場合でも、キャプチャなしでなければならなかった
キャプチャなし
デフォルト引数
そのようなラムダ式をキャプチャとともに許可する
キャプチャ
CWG 2509 C++17 各指定子は指定子シーケンス内に複数回出現できた
複数回出現
各指定子は指定子シーケンス内に最大1回しか出現できない
最大1回しか出現できない
指定子シーケンス
CWG 2561 C++23 明示的なオブジェクトパラメータを持つラムダは、望ましくない関数ポインタ型への変換関数を持つことができた
変換関数
そのような変換関数を持たない
変換関数
CWG 2881 C++23 明示的なパラメータを持つoperator()は、継承がpublicではないか曖昧である場合に派生クラスに対してインスタンス化できた
インスタンス化できた
ill-formed にした
P0588R1 C++11 暗黙的なラムダキャプチャのルールはodr-useを検出した 検出が簡素化された

[編集] 参照

auto 指定子 (C++11) 式から推論される型を指定します [編集]
(C++11)
コピー構築可能な任意の呼び出し可能オブジェクトをラップするコピー可能なラッパー
(クラステンプレート) [編集]
与えられた呼び出しシグネチャで修飾子をサポートする任意の呼び出し可能オブジェクトのムーブ専用ラッパー
(クラステンプレート) [編集]

[編集] 外部リンク

ネストされた関数 - 別の (囲む) 関数内に定義された関数。
English 日本語 中文(简体) 中文(繁體)