Search code examples
c++c++-concepts

How can I implement logical or for existence of one of a list of member functions in a 'requires' expression?


I have a generic function which can accept types which have at least one of a list of member functions.

I am trying to write an inline requires expression to validate this.

I have an example below which fails to compile because neither of my types pass the requires clause

#include <iostream>

struct Foo
{
    bool has_foo() const { return true; }
};

struct Bar
{
    bool has_bar() const { return true; }
};


template<typename T>
bool check(const T& t)
    requires requires (T t)
    {
        { t.has_foo() || t.has_bar() };
    }
{
    if constexpr (requires { t.has_foo(); })
    {
        if (t.has_foo())
            std::cout << "has foo\n";
        else
            std::cout << "no foo\n";
    }
    if constexpr (requires { t.has_bar(); })
    {
        if (t.has_bar())
            std::cout << "has bar\n";
        else
            std::cout << "no bar\n";
    }
    return true;
}

int main()
{
    check(Foo());
    check(Bar());
    return 0;
}

The build fails due to:

test.cpp:40:10: error: no matching function for call to 'check(Foo)'
   40 |     check(Foo());
      |     ~~~~~^~~~~~~
test.cpp:15:6: note: candidate: 'template<class T> bool check(const T&) requires requires(T t) {{t.has_foo() || t.has_bar()};}'
   15 | bool check(const T& t)
      |      ^~~~~
test.cpp:15:6: note:   template argument deduction/substitution failed:
test.cpp:15:6: note: constraints not satisfied
test.cpp: In substitution of 'template<class T> bool check(const T&) requires requires(T t) {{t.has_foo() || t.has_bar()};} [with T = Foo]':
test.cpp:40:10:   required from here
test.cpp:15:6:   required by the constraints of 'template<class T> bool check(const T&) requires requires(T t) {{t.has_foo() || t.has_bar()};}'
test.cpp:16:14:   in requirements with 'T t' [with T = Foo]
test.cpp:18:23: note: the required expression '(t.has_foo() || t.has_bar())' is invalid, because
   18 |         { t.has_foo() || t.has_bar() };
      |           ~~~~~~~~~~~~^~~~~~~~~~~~~~
test.cpp:18:28: error: 'struct Foo' has no member named 'has_bar'
   18 |         { t.has_foo() || t.has_bar() };
      |                          ~~^~~~~~~

How can I turn my requires expression

requires (T t)
{
    { t.has_foo() || t.has_bar() };
}

into a logical or expression, such that types which meet either of the expressions are allowed?


Solution

  • You can split it into two requires expressions, which can be combined with logical OR:

    template<typename T>
    bool check(const T& t)
        requires(
            requires (T t) { t.has_foo(); } ||
            requires (T t) { t.has_bar(); }
            )
    {
        // [...]
    }
    

    If that looks too convoluted, you can also define that as a new concept:

    template<typename T>
    concept has_foo_or_bar =
        requires (T t) { t.has_foo(); } ||
        requires (T t) { t.has_bar(); };
    
    template<typename T>
    bool check(const T& t)
        requires( has_foo_or_bar<T>(t) )
    {
        // [...]
    }
    

    Compiler Explorer