C++14で導入された汎用ラムダキャプチャ(generalized lambda capture)と、コピー不可かつムーブ可能オブジェクトの扱いについてメモ。
汎用ラムダキャプチャ構文でムーブキャプチャしたstd::unique_ptr
型の変数up1
を、そのラムダ式中でさらに変数up2
へムーブしようとするとコンパイルエラーを引き起こす。
// C++14以降 #include <memory> auto up0 = std::make_unique<int>(42); auto lm = [up1 = std::move(up0)] { auto up2 = std::move(up1); // NG: ill-formed };
ラムダ本体は暗黙にクロージャ型(closure type)の const メンバ関数、つまり前掲例ではvoid ClosureType::operator()() const
相当であり、ムーブキャプチャされた変数up1
の型はconst std::unique_ptr<int>
となる。よってラムダ本体のup2 = std::move(up1)
ではコピーコンストラクタが選択されるが、std::unique_ptr
クラスはコピー不可のためコンパイルエラーとなる。
ラムダ式宣言で mutable キーワードを使用すれば、期待通りの動作を実現できる。ただし、クロージャオブジェクトlm
の1回目の呼び出しでup1
はムーブ済みの状態となるため、2回以上呼び出すときはその振る舞いに留意すること。(→id:yohhoy:20120616)
auto up0 = std::make_unique<int>(42); auto lm = [up1 = std::move(up0)]() mutable { // ★ auto up2 = std::move(up1); // OK: well-formed }; lm(); // up1からup2へムーブ(その後 破棄) // <lm中のup1>.get()==nullptr lm(); // up1==up2==nullptr
ノート:ラムダ式宣言に mutable キーワードを指定する場合、パラメータ宣言部の括弧()
が構文上必須となる。(→id:yohhoy:20120117)
N3937 5.1.2/p5より一部引用。
The closure type for a non-generic lambda-expression has a public inline function call operator (13.5.4) whose parameters and return type are described by the lambda-expression's parameter-declaration-clause and trailing-return-type respectively. (snip) This function call operator or operator template is declared
const
(9.3.1) if and only if the lambda-expression's parameter-declaration-clause is not followed bymutable
. (snip)
関連URL