質問: requires(T t){ t.f(); }; template void PrintIfPrintable(T arg) { if constexpr (Printable){ //bool a {arg.f()}; // コンパイルエラー std::cout << arg.f() << std::endl; } } int main() { PrintIfPrintable(A()); } ```
回答: (直接的な解決策はsayuriさん回答を参照ください。本回答は追加の情報提供となります。) > > C++20 コンセプトで、特定の戻り値の型を持つメンバ関数を持つことを要求するにはどのようにすべきですか? > > > 関数テンプレート定義中の処理において該当メンバ関数`f()`の戻り値をどう扱うかに合わせて、2種類の回答がありえます。 * (1) 厳密に`bool`型を要求する。 * (2) `bool`型のように扱えればよい。 ``` template concept Printable = requires(T t) { // (1) T::f()は厳密にbool型を返す { t.f() } -> std::same_as }; template concept Printable = requires(T t) { // (2) T::f()は「bool型に変換可能な型」を返す { t.f() } -> std::convertible_to }; ``` (1)のデメリットとして、`f()`の戻り値型が厳密に`bool`型を要求するため、例えば`const bool&`を返すようなケースでコンセプト`Printable`を満たさないと判定されます。 一方で(2)のデメリットとして、例示コードのような`int`→`bool`縮小変換をチェックしません。つまり`int`型は`bool`型に(明示)変換可能と判定されます。とはいえ、真偽値(`bool`)を要求する`if (t.f()) {...}`のような文脈での利用では、大抵はこのような判定で問題ないと思います。 > > コメント:複数の条件を指定したい場合は、template concept Printable = requires(T t){ t.f(); } && std::same_as;のように&&で条件を追加していくのが正攻法でしょうか? > > > C++20コンセプトにおいては、下記いずれの書き方も同じ意味となります。 * `requires(T t){ t.f(); } && std::same_as().f()), bool>` * `requires(T t){ { t.f() } -> std::same_as; }` C++17以前のSFINAE技法では、まず最初に式`t.f()`が有効であることを確認し、続いて式`t.f()`が型`bool`と等しいか否かを確認する必要がありました。(コメントはこの考え方を反映したものでしょう) C++20コンセプトでは、定義中に登場する式`t.f()`が無効であればその時点で制約式全体が偽とみなされます。SFINAE技法のようにコンパイルエラーを引き起こさないため、より直接的な制約記述が可能となっています。