Search code examples
c++c++20c++-concepts

Is there a simpler way to write a concept that accepts a set of types?


Essentially, is there a shorter/cleaner way to define Alphabet than using a bunch of std::same_as/std::is_same?

struct A {};
struct B {};
struct C {};
...

template <typename T>
concept Alphabet =
    std::same_as<T, A> ||
    std::same_as<T, B> ||
    std::same_as<T, C> ||
    ...

You could accomplish this (sort of) by defining a base class and using std::is_base_of, but for the sake of this question let's assume A, B, C, etc. cannot be modified.


Solution

  • Using Boost.Mp11, this is a short one-liner as always:

    template <typename T>
    concept Alphabet = mp_contains<mp_list<A, B, C>, T>::value;
    

    Or could defer to a helper concept (or a helper variable template or a helper whatever):

    template <typename T, typename... Letters>
    concept AlphabetImpl = (std::same_as<T, Letters> or ...);
    
    template <typename T>
    concept Alphabet = AlphabetImpl<T, A, B, C>;
    

    However, note that any other implementation other than the painfully rote one:

    template <typename T>
    concept Alphabet = same_as<T, A> or same_as<T, B> or same_as<T, C>;
    

    Leads to differing behavior with regards to subsumption. This probably doesn't matter, but it might:

    template <Alphabet T>   void f(T); // #1
    template <same_as<A> T> void f(T); // #2
    
    f(A{}); // with the repeated same_as or same_as or ..., this calls #2
            // with any other nicer implementation, ambiguous