問題
ラムダ式をオーバーロード風に扱いたい場合など、ラムダ式でもSFINAEで呼び出しを制御したい場合がある
この際、どう書けばよいかについてまとめる
解決
通常の関数の場合から考えると
- 引数部
- 戻り値型部
- テンプレート引数部
の3種類が思いつく。これらについてひとつずつ考えていく
引数部でSFINAEする
引数の末尾にsfinaeするための部分を付ける
個人的にもっともおすすめ
例.cpp
std::visit(mylib::overload{
[](std::string s){return s; },
[](auto s, decltype(std::to_string(s))* =0){return std::to_string(s); } //<--to_stringできる奴だけ
}, v);
[](auto s, /*SFINAEしたい項目*/ * =0){/*略*/; }
*
=
の2記号は間を開ける必要があることに注意(くっつけると *=
に解釈されて文法ミスになる)
- 利点
- 戻り値型を明記しなくて済む
- 戻り値型部でやる場合、戻り値型の推論が効かなくなるため自分で書く必要が出てくる
- 戻り値型を明記しなくて済む
- 欠点
- 見た目が若干キモイ
戻り値部でSFINAEする
例.cpp
std::visit(mylib::overload{
[](std::string s){return s; },
[](auto s)->decltype(std::to_string(s)){return std::to_string(s); } //<--to_stringできる奴だけ
}, v);
[](auto s)->/*SFINAEしたい項目+戻り値型*/{/*略*/ }
- 利点
- 上の例の様な「処理XができるかをSFINAEで調べて、できたら処理Xを呼ぶ」みたいなケース、その場合これの見た目が一番キモくない
- ラムダ式でsfinaeを使う場合、このパターンは結構あると思われる
- また、戻り値の型に興味がない場合でも
- 上の例の様な「処理XができるかをSFINAEで調べて、できたら処理Xを呼ぶ」みたいなケース、その場合これの見た目が一番キモくない
- 欠点
- 戻り値の型推論が利かなくなるため、SFINAEの条件と戻り値の型が全然違う場合、見た目がキモくなるor辛くなる
- 要するに「汎用でない」
- 戻り値の型推論が利かなくなるため、SFINAEの条件と戻り値の型が全然違う場合、見た目がキモくなるor辛くなる
テンプレート引数部でSFINAEする
テンプレート引数部なんてものはラムダ式にない。したがってここでSFINAEする方法もない
少なくともC++17現在では
C++20ではラムダ式にもテンプレート引数部が存在するようになるといわれている