如何使用 SFINAE 为枚举 class 中的缺失值制作 polyfill?

How to use SFINAE to make a polyfill for missing values in enum class?

我认为这应该很容易,但我已经为此苦苦挣扎了一段时间,所以我想我应该在这里问一下。

我想制作一个模板元函数,它采用对应于 C++11 枚举 class 的类型作为其参数,并且 returns 是一个 int:

然后我想制作一个模板函数,它接受一些枚举 class E 的 run-time 实例,将其静态转换为 int,并检查它是否与此元函数匹配.

我尝试了很多次迭代,模板化一个结构并使用模板偏特化来尝试区分 E::a 是否存在,也使用函数模板...我不确定我是否可以重建我所做的一切尝试过,但这是最近的迭代:

template <typename E>
inline int get_a_int_val(int result = E::a) { return result; }

template <typename E>
inline int get_a_int_val(int result = 42) { return result; }

template <typename E>
inline bool is_a_val(const E & input) {
  return static_cast<int>(input) == get_a_int_val<E>();
}

这不起作用,因为我正在重新定义默认参数。

template <typename E, int result = E::a>
inline int get_a_int_val() { return result; }

template <typename E, int result = 42>
inline int get_a_int_val() { return result; }

template <typename E>
inline bool is_a_val(const E & input) {
  return static_cast<int>(input) == get_a_int_val<E>();
}

这不起作用,因为 non-type 参数不能依赖于类型参数。

template <typename E>
struct get_a_int_val {
  static const int value = 42;
};

template <typename E>
struct get_a_int_val<E> {
  static const int value = static_cast<int>(E::a);
};

template <typename E>
inline bool is_a_val(const E & input) {
  return static_cast<int>(input) == get_a_int_val<E>::value;
}

这行不通,因为

error: 
  class template partial specialization does not specialize any template
  argument; to define the primary template, remove the template argument
  list

正确的做法是什么?


动机:

我想这样做的原因是,我想解决我在此处报告的 libstdc++ 中似乎存在的错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68307

<system_error> header 中的 C++11 中,有一堆 std::errc 的枚举值应该被定义,然而,在 mingw 上,其中一些缺失。这会导致我的程序出现编译错误,因为根据 Asio 的配置方式,lib::asio::errc 可能会被类型定义为 std::errc,并且 websocketpp 假定 lib::asio::errc::operation_canceled是定义的符号。我想把一些可以放在 websocketpp 代码中的垫片放在一起,这样它就可以在任何平台上被可接受地定义(如果它存在,或者到 lib::asio::errc::operation_canceled,或者从 <cerrno> 如果没有。)

您可以为此创建特征:

template <typename E>
std::false_type has_a_impl(...);

template <typename E> auto has_a_impl(int) -> decltype(E::a, std::true_type{});

template <typename E>
using has_a = decltype(has_a_impl<E>(0));

然后在SFINAE中使用:

template <typename E>
std::enable_if_t<has_a<E>::value, int>
get_a_int_val() { return static_cast<int>(E::a); }

template <typename E>
std::enable_if_t<!has_a<E>::value, int>
get_a_int_val() { return 42; }

Demo

您可以通过多种方式做到这一点,其中之一是:

template <typename E, typename Enable = void>
struct get_a_int_val {
  static const int value = 42;
};

template <typename E>
struct get_a_int_val<E, typename std::enable_if<std::is_same<decltype(E::a), 
                                                decltype(E::a)>::value, void>::type>{
  static const int value = static_cast<int>(E::a);
};

LIVE DEMO