yohhoyの日記

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

std::generator<T/T&&/T&/const T&>

次期C++2b(C++23)標準ライブラリのコルーチンサポート型std::generator<Ref>(→id:yohhoy:20220801)の第1テンプレートパラメータRefと、オブジェクトのコピー/ムーブの関係性についてメモ。

まとめ:

  • 利用側にconst参照を提供:const-lvalue参照const T&を指定。
  • 利用側に可変参照を提供:lvalue参照T&を指定。
  • ムーブのみ(Move-only)型:値型Tまたはrvalue参照型T&&を指定。
    • 実動作は両者で同一。“ムーブ” 動作を示唆するT&&の方が好ましい?
  • プリミティブ型など*1:値型Tを指定。
  • 利用側の範囲for:auto&&(→id:yohhoy:20120609)またはconst auto&を推奨。

テンプレートパラメータRefに値型Tまたはその参照型を指定した場合*2、各箇所において発生するコピー/ムーブは下表のとおり:

Ref yield-v yield-cv yield-rv consume-v
T 1 copy 1 copy 0 1 move
T&& 1 copy 1 copy 0 1 move
T& 1 copy 1 copy (ill-formed) 1 copy
const T& 0 0 0 1 copy

凡例:0=コピー/ムーブなし、(ill-formed)=コンパイルエラー

// C++2b
#include <generator>

// ユーザ定義コルーチン
std::generator<??> gen()
{
  T v;
  co_yield v;   // yield-v
  const T cv;
  co_yield cv;  // yield-cv

  // yield-rv
  co_yield T{};
  co_yield std::move(v);
}

// 利用側コード
void consumer()
{
  // consume-v
  for (auto v: gen()) { ... }

  // 下記2パターンは常にコピー/ムーブ発生しない
  for (const auto& cr: gen()) { ... }
  for (auto&& ur: gen()) { ... }
}

ノート:コルーチン内でco_yield std::move(v);を記述しても、即座にはムーブ処理が行われないことに留意。Ref=TT&&であれば利用側コードでムーブ処理が行われる。

関連URL

*1:コピー処理コストの安い型:std::string_view や std::span<T> 型も含まれる。

*2:C++ムーブセマンティクスの文脈では const T&& 型には使い道がない。