今さらですが最近c++20の機能を色々触っています。
そしたらいつの間にかラムダ式が少し強化されていたので今回はそれに関する小ネタを。
decltype([]{})できるやん
c++17までラムダ式のdecltypeを取るにはいったんラムダを変数に格納した後その変数をdecltypeする必要がありました。
auto something = []{ cout << "hello" << endl; };
auto func = std::make_shared<decltype(something)>();
(*func)();
しかし、c++20では直接ラムダを渡すことができます。
auto func = std::make_shared<decltype([]{ cout << "hello" << endl; })>();
(*func)();
もちろんラムダは定義するたびに別の型となりますのでシグネチャが同じでも代入はできません。
auto func = std::make_shared<decltype([]{})>();
func = std::make_shared<decltype([]{})>(); // ラムダの型が違うのでfuncには代入できない
関数クラステンプレートパラメータにラムダを記述する
ラムダをテンプレートパラメータに記述できるようになってよく見かけるのがstlの関数クラスパラメータをラムダ化したものでしょうか
// mapの比較クラスをラムダに置き換えたもの(less<int>相当)
map<int, int, decltype([](int a, int b) {return a < b; }) > { {0, 1}, { 1, 2 } };
// unique_ptr の削除子クラスをラムダにしたもの(削除直前に標準出力付き)
unique_ptr<string, decltype([](string* s) {cout << *s << endl; delete s; })> ptr(new string("hoge"));
見づらいですがこのようにワンライナーで指定できています。
ラムダでコンセプト
以上を踏まえてある型Tに対してラムダで定義したコードがパスするかどうかを判定する処理をワンライナーで作ってみます。
int main() {
// stringのメンバ関数c_str() constがあるかをチェックする
static_assert(is_invocable_v<decltype([](const auto& _) -> decltype(_.c_str()) {}), string>);
return 0;
}
やってることはそんな新しいことじゃないですけどテンプレートパラメータだけでコンセプトのようなものが実現できるようになってます。
とはいえラムダの記述性はコンセプトに及ばないんで所詮劣化コンセプトなんですが、ちょっとした記述が必要なところでラムダで書けるのは非常に便利なのではないでしょうか?
これで以前書いた記事【再考】メンバの存在を調べるでできなかった事前定義不要の完全なメタプログラミング向けメンバ判定ができるようになりました。
ちなみに
c++17でラムダを直接テンプレートパラメータに書けなかったのはラムダをコンセプトのように乱用されることを懸念してのことでした。
なのでこの記事は思いっきりよろしくないことを言っているので黒魔術の一種だと思ってくださいw