在多个模板上重复模板 enable_if 条件

Repeat template enable_if condition on multiple templates

我有以下代码...

#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;
}

... 为各种枚举类型实现 operator<<。现在具体行:

typename = std::enable_if_t<is_contained<T, Fruit>::value>>

确保我的流运算符实现仅用于我的特定类型,该类型将扩展为我所有用户枚举类型的列表。

我要解决的问题是如何在其他模板函数上重用该条件,而不必重复类型列表或使用宏。类似于:typename = TypeIsInMyList<T>

可能很简单,但我想不出什么。

中找到 is_contained 实现

如果我没理解错的话,你正在寻找一个简单的using,比如

template <typename T>
using TypeIsInMyList
   = std::enable_if_t<is_contained<T, Fruit, void, int, char>::value>;

您可以按如下方式使用

template <typename T,
          typename = TypeIsInMyList<T>>

但是我建议修改如下

template <typename T>
using TypeIsInMyList
   = std::enable_if_t<is_contained<T, Fruit, void, int, char>::value, int>;

并用在等号的左边

template <typename T,
          TypeIsInMyList<T> = 0>

有点复杂但是...我不认为这有什么真正的区别,在你的情况下,但是当你在重载中有替代函数时,第二种解决方案是更好的方法。

这是 C++20 的处理方式:

template <typename T, typename ...P>
concept one_of = (std::same_as<T, P> || ...);

那你可以直接这样写:

template <one_of<Fruit, void, int, char> T>
std::ostream &operator<<(std::ostream &os, const T &t) {/*...*/}

或者再做一个概念:

template <typename T>
concept foo = one_of<T, Fruit, void, int, char>;

template <foo T>
std::ostream &operator<<(std::ostream &os, const T &t) {/*...*/}