1

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.

Daskie
  • 435
  • 3
  • 11
  • 3
    Several generalized functions are given as answers to [this question](https://stackoverflow.com/questions/56720024/how-can-i-check-type-t-is-among-parameter-pack-ts). – Drew Dormann May 13 '22 at 19:18

1 Answers1

5

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
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Shame, I hoped I had missed a more "direct" notation, something like `concept Alphabet = A; B; C;`. That's a good point about the subsumption though, thank you – Daskie May 13 '22 at 19:32