简体   繁体   中英

Repeat template enable_if condition on multiple templates

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.

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) {/*...*/}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM