yohhoyの日記

技術的メモをしていきたい日記

ラムダキャプチャ中での前置ellipsisと後置ellipsis

プログラミング言語C++において可変長引数テンプレートパラメータパックをラムダ式でキャプチャする際、そのキャプチャ方式によってellipsis(...)の前置/後置が異なることに注意。

template <typename... Ts>
void func(Ts... xs)
{
  // C++11以降: 簡易キャプチャ(simple-capture)
  [xs...] {
    // xsの各要素をコピーキャプチャ
  };

  [&xs...] {
    // xsの各要素を参照キャプチャ
  };

  // C++2a: 初期化キャプチャ(init-capture)
  [...xs = std::move(xs)] {
    // xsの各要素をムーブキャプチャ
  };
}

N4800(C++2a WD) 7.5.5.2 構文規則, p17より一部引用。
→ 2021-05-11追記:本記事の内容には直接影響しないが、C++20言語仕様では P2095R0 採択により下記引用と少し異なる文法定義となっている。初期化キャプチャ中における参照キャプチャのパック展開構文を[&...xs = init]へ修正している。

lambda-capture :
  capture-list
  (snip)
capture-list :
  capture
  capture-list , capture
capture :
  simple-capture ...opt
  ...opt init-capture

A simple-capture followed by an ellipsis is a pack expansion (12.6.3). An init-capture preceded by an ellipsis is a pack expansion that introduces an init-capture pack (12.6.3) whose declarative region is the lambda-expression's compound-statement. [Example:

template<class... Args>
void f(Args... args) {
  auto lm = [&, args...] { return g(args...); };
  lm();

  auto lm2 = [...xs=std::move(args)] { return g(xs...); };
  lm2();
}

-- end example]

メモ:P0780R1時点では初期化キャプチャ(init-capture)も後置ellipsis方式だったが、最終的に前置ellipsisに変更されてC++2a言語仕様に採択された。“導入される名前直前にellipsisを記述” という既存構文との一貫性を重視したのこと。*1

関連URL

*1:P0780R2: "Following Core and Evolution guidance, the ellipses for an init-capture pack have been moved from following the init-capture to preceding it. This is consistent with the existing practice of ... preceding the name that it introduces."