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 wrote:

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

Leave a Reply