如何使用SFINAE为枚举类中缺少的值进行polyfill

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

本文关键字:polyfill SFINAE 何使用 枚举      更新时间:2023-10-16

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

我想制作一个模板元函数,它以一个与C++11枚举类对应的类型作为参数,并返回一个int:

  • 如果枚举类E具有枚举器值a,则返回static_cast<int>(E::a)
  • 如果枚举类E没有枚举器值a,则返回42

然后,我想制作一个模板函数,它接受某个枚举类E的运行时实例,将其静态强制转换为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>();
}

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

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++中的一个bug,我在这里报告了这个bug:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68307

在C++11中,<system_error>标头中有一组std::errc的枚举器值,这些值本应定义,但在mingw中,其中一些值丢失了。这会在我的程序中导致编译错误,因为根据Asio的配置方式,lib::asio::errc可能被类型定义为std::errc,而websocketpp假设lib::asio::errc::operation_canceled是一个定义的符号。我想把某种垫片放在websocketpp代码中,这样它就可以在任何平台上被定义为可接受的(如果存在,则定义为lib::asio::errc::operation_canceled,如果不存在,则从<cerrno>定义为ECANCELED)

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

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

现场演示

您可以为此创建一个特征:

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

演示