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.
CodePudding user response:
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