C++20 コア言語機能読書会vol.17
開催日時
2022年11月2日(水) 21:00~22:30
開催URL
参加人数
1人
ウォーミングアップ
5.3 ステートレスラムダのクロージャ型のデフォルトコンストラクタと代入演算子の定義
C++17までクロージャ型は以下は定義されていた
コピーコンストラクタ
ムーブコンストラクタ
デストラクタ
以下は定義されていなかった
デフォルトコンストラクタ
代入演算子
std::unique_ptrみたいなテンプレートパラメータでカスタムするクラスにラムダ式を渡す場合にはひと手間かける必要があった
code:従来.cpp
auto custom_deleter = [](auto h) { close_handle(h); };
std::unique_ptr<handle_t, decltype(custom_deleter)> handle{&h,custom_deleter};
本当はこう書きたい
code:理想.cpp
auto custom_deleter = [](auto h) { close_handle(h); };
std::unique_ptr<handle_t, decltype(custom_deleter)> handle{&h};
ラムダ式のインスタンスをどこからも受け取らないのでテンプレートクラスの中で生成する必要がある
だが、デフォルト構築可能ではないのでインスタンスは生成できないし、外部からインスタンスを受け取らないのでコピー/ムーブコンストラクタも使えない
デフォルト構築できたとしても、その後代入ができない
C++20からステートレス(キャプチャをしていない)クロージャ型は以下を持つようになる
デフォルトコンストラクタ
代入演算子
ローカル変数なくしたいね
5.4 評価されない文脈におけるラムダ式
C++17までラムダ式は評価されない文脈に現れることが禁止されていた
以下のオペランド
decltype
sizeof
noexcept
typeid
requires式(C++20~)
コンセプト定義内(C++20~)
何のために禁止?
ラムダ式によってSFINAEすることを禁止するため
この制限は後で別の形に書き直されたので禁止する必要がなくなった
5.3のコードからさらにローカル変数を使わず直接書けるようになった
code:cpp
std::unique_ptr<handle_t, decltype([](auto h) { close_handle(h); })> handle {&h};
ワンライナーを簡単に書けるようになった
5.5 初期化キャプチャにおけるパック展開
パラメータパックが入ったテンプレートの場合、展開してキャプチャすることはできた
code:cpp
template<class F, class... Args>
auto delay_invoke1(F f, Args... args) {
// パラメータパックの各要素をコピーキャプチャする、ok
return std::invoke(f, args...);
};
}
初期化(move)キャプチャすることはできなかった
code:cpp
template<class F, class... Args>
auto delay_invoke2(F f, Args... args) {
// より効率的にムーブする、ng
return std::invoke(f, args...);
};
}
C++14時点の仕様では解決が難しい問題があり許可されていなかった
「C++14 当初の仕様では、~初期化キャプチャでパック展開するとクラスのメンバとしてパラメータパックが導入されることになりました」
キャプチャした変数はクロージャオブジェクトのメンバ変数と見なされる
code:cpp
template <typename T> void call_f(T t) {
f(t.x...);
}
Tがクロージャ型として、そのメンバ変数xがパラメータパックだったとすると可変長テンプレート外でパック展開される可能性を考えないといけなくなる
名前付きメンバが提供されないと解決するのはなぜ?上のコードみたいに呼び出せなくなるから?
現在はその問題は解決しているので、この制限が撤廃された
5.6 構造化束縛した変数のキャプチャ
次回ここから
お悩み雑談室