Reputation: 20255
I have the following code...
#include <iostream>
#include <map>
template <typename T, typename...>
struct is_contained : std::false_type {};
template <typename T, typename Head, typename... Tail>
struct is_contained<T, Head, Tail...>
: std::integral_constant<bool, std::is_same<T, Head>::value ||
is_contained<T, Tail...>::value> {};
// Enum types
enum class Fruit { APPLE, BANANA };
// To string maps
std::map<Fruit, std::string> fruits = {{Fruit::APPLE, "apple"},
{Fruit::BANANA, "banana"}};
// Map string map to enum
template <typename T>
std::map<T, std::string> typeMap;
template <>
std::map<Fruit, std::string> typeMap<Fruit> = fruits;
// operator<< for mapped enums
template <typename T,
typename = std::enable_if_t<is_contained<T, Fruit>::value>>
std::ostream &operator<<(std::ostream &os, const T &t) {
os << typeMap<T>.at(t);
return os;
}
int main() {
Fruit f = Fruit::BANANA;
std::cout << f << std::endl;
return 0;
}
... to implement operator<<
for various enum types. Now the specific line:
typename = std::enable_if_t<is_contained<T, Fruit>::value>>
makes sure that my stream operator implementation is only used for my specific type which would be expanded to a list of all of my user enum types.
The problem I am trying to solve is how to reuse that condition on other template functions without having to repeat the list of types or to use macros. Something like: typename = TypeIsInMyList<T>
Might be very simple, but I failed to come up with something.
The is_contained
implementation was found in https://stackoverflow.com/a/16252940/11722.
Upvotes: 1
Views: 247
Reputation: 96286
Here's the C++20 way of doing things:
template <typename T, typename ...P>
concept one_of = (std::same_as<T, P> || ...);
Then you can either directly write this:
template <one_of<Fruit, void, int, char> T>
std::ostream &operator<<(std::ostream &os, const T &t) {/*...*/}
Or make yet another concept:
template <typename T>
concept foo = one_of<T, Fruit, void, int, char>;
template <foo T>
std::ostream &operator<<(std::ostream &os, const T &t) {/*...*/}
Upvotes: 4
Reputation: 66200
If I understand correctly, you're looking for a simple using
, like
template <typename T>
using TypeIsInMyList
= std::enable_if_t<is_contained<T, Fruit, void, int, char>::value>;
that you can use as follows
template <typename T,
typename = TypeIsInMyList<T>>
But I suggest to modify it as follows
template <typename T>
using TypeIsInMyList
= std::enable_if_t<is_contained<T, Fruit, void, int, char>::value, int>;
and use it on the left of the equal
template <typename T,
TypeIsInMyList<T> = 0>
It's a little complicated but... I don't think it's a real difference, in you case, but the second solution is the a preferable way when you have alternative function in overloading.
Upvotes: 3