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

Posted

技术标签:

【中文标题】如何使用 SFINAE 为枚举类中的缺失值制作 polyfill?【英文标题】:How to use SFINAE to make a polyfill for missing values in enum class? 【发布时间】:2015-11-12 19:15:57 【问题描述】:

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

我想制作一个模板元函数,它将对应于 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++ 中的一个错误:https://gcc.gnu.org/bugzilla/show_bug.cgi?id=68307

在 C++11 的 &lt;system_error&gt; 标头中,有一堆 std::errc 的枚举值应该被定义,但是,在 mingw 上它们中的一些丢失了。这会导致我的程序出现编译错误,因为根据Asio 的配置方式,lib::asio::errc 可能会被类型定义为std::errc,而websocketpp 假定lib::asio::errc::operation_canceled 是一个已定义的符号。我想拼凑一些可以在websocketpp 代码中放置的垫片,以便在任何平台上都可以接受定义(如果存在,则定义为lib::asio::errc::operation_canceled,如果不存在,则从&lt;cerrno&gt; 删除.)

【问题讨论】:

【参考方案1】:

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

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

【讨论】:

【参考方案2】:

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

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

【讨论】:

以上是关于如何使用 SFINAE 为枚举类中的缺失值制作 polyfill?的主要内容,如果未能解决你的问题,请参考以下文章

Swift - 如何使用枚举为 UITableView 制作自定义标题部分?

类模板构造函数中的 SFINAE

有没有办法将类中的枚举属性设置为所有可用的枚举?

如何从 A 类中的 JTextField 输入向 B 类中的数据库插入值

如何使用在同一类的基模板中的类中声明的枚举

如何区分空值字段与杰克逊库中的缺失字段