74203


A.f()はintを返していますが、もしコメントアウト部が入っているコードの場合は、boolでなければコンパイルエラーになります。これをコンセプトで要求したい（f()の戻り値をboolであることを要求したい)場合はどのようにすべきでしょうか？

[code]

    #include <iostream>
    #include <string>
    #include <concepts>
    
    struct A{
        int f()
        {
            return 42;
        }
    };
    
    template<class T>
    concept Printable = requires(T t){
        t.f();
    };
    
    template<typename T>
    void PrintIfPrintable(T arg)
    {
        if constexpr (Printable<T>){
            //bool a {arg.f()}; // コンパイルエラー
            std::cout << arg.f() << std::endl;
        }
    }
    
    int main()
    {
        PrintIfPrintable(A());
    }
    
[/code]


CC BY-SA 4.0
2021-02-22T09:05:27.773
74200
2021-02-24T10:41:42.163
37013
question
c++
C++20 コンセプトで、特定の戻り値の型を持つメンバ関数を持つことを要求するにはどのようにすべきですか？


[`std::same_as`コンセプト](https://cpprefjp.github.io/reference/concepts/same_as.html)を使用して`t.f()`の戻り値が`bool`か判定する感じでしょうか？

[code]

    template<class T>
    concept Printable = std::same_as<decltype(std::declval<T>().f()), bool>;
    
[/code]


CC BY-SA 4.0
2021-02-22T09:30:52.337
74203
2021-02-22T09:30:52.337
4236
74200
answer


（直接的な解決策はsayuriさん回答を参照ください。本回答は追加の情報提供となります。）

> C++20 コンセプトで、特定の戻り値の型を持つメンバ関数を持つことを要求するにはどのようにすべきですか？

関数テンプレート定義中の処理において該当メンバ関数`f()`の戻り値をどう扱うかに合わせて、2種類の回答がありえます。

  * (1) 厳密に`bool`型を要求する。
  * (2) `bool`型のように扱えればよい。

[code]

    template<class T>
    concept Printable = requires(T t) {
        // (1) T::f()は厳密にbool型を返す
        { t.f() } -> std::same_as<bool>
    };
    
    template<class T>
    concept Printable = requires(T t) {
        // (2) T::f()は「bool型に変換可能な型」を返す
        { t.f() } -> std::convertible_to<bool>
    };
    
[/code]

(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<decltype(std::declval().f()), bool>;のように&&で条件を追加していくのが正攻法でしょうか？

C++20コンセプトにおいては、下記いずれの書き方も同じ意味となります。

  * `requires(T t){ t.f(); } && std::same_as<decltype(std::declval<T>().f()), bool>`
  * `requires(T t){ { t.f() } -> std::same_as<bool>; }`

C++17以前の[SFINAE技法](https://ja.wikipedia.org/wiki/SFINAE)では、まず最初に式`t.f()`が有効であることを確認し、続いて式`t.f()`が型`bool`と等しいか否かを確認する必要がありました。（コメントはこの考え方を反映したものでしょう）

C++20コンセプトでは、定義中に登場する式`t.f()`が無効であればその時点で制約式全体が偽とみなされます。SFINAE技法のようにコンパイルエラーを引き起こさないため、より直接的な制約記述が可能となっています。


CC BY-SA 4.0
2021-02-24T10:32:19.377
74247
2021-02-24T10:41:42.163
2021-02-24T10:41:42.163
49
49
74200
answer