使用C++11模板SFINAE实现enum class与整数的比较

在C++11中添加了enum class类型,与enum类型不同的是,使用enum class中的枚举值时必须加上类型限定符,而原来的enum枚举是可以不用加类型限定符的,它们都会被当作全局(如果没有在命名空间内)整数来对待。这样就会导致不同的模块,不能定义相同的枚举值。

比如:
头文件a.h中定义了一个枚举:

enum ETest1
{
    
    
	A,
	B,
};

头文件b.h需要包含a.h,如果此时在b.h中定义:

enum ETest2
{
    
    
	A,
	B,
};

则会导致编译不过,提示A与B重定义。
在这里插入图片描述
所以C++11添加了enum class,如果把前面的enum改为enum class则是没有问题的。此时的枚举值在使用的时候必须加类型限定符,且不会被自动当作整数来对待。

如果有下面的代码:

if (1 == ETest1::A || ETest1::A == 1)
{
    
    
}

则会报错:
在这里插入图片描述

此时需要把整数强制转换成枚举类型或者把枚举类型强制转换成整数类型才能进行比较。

if (1 == (int)ETest1::A || ETest1::B == (ETest1)2)
{
    
    
}

这就要我们每次在比较的时候都需要多写一些代码来进行强制转换,这也是C++11设计enum class时故意要求这么做的。

如果想省事,有没有办法做到与普通的enum一样可以直接与整数进行比较呢?

掌握C++11模板SFINAE特性的话就可以很方便地做到,可以看看笔者的上篇文章使用C++11模板SFINAE特性重写序列化与反序列化,有介绍与使用C++11模板SFINAE特性。

SFINAE是Substitution Failure Is Not An Error的缩写,直译为中文是:替换失败不是错误,cppreference中的定义为:

在函数模板的重载决议中:为模板形参替换推导类型失败时,从重载集抛弃特化,而非导致编译错误。

我的理解就是编译器在匹配重载函数时,通过类型的推导可能匹配到类型不一致的重载函数而导致匹配失败,但是只要最后能正确匹配到一个重载函数,编译器就不会报错。使用过Erlang语言的读者会对函数匹配有比较深刻的理解。

主要是使用std::enable_if_t或者enable_if模板函数来让编译器对函数进行匹配。

std::enable_if 或者enable_if_t可以使用在以下三个方面:

  • 作为函数参数
  • 作为模板参数
  • 作为返回类型

这里附上实现enum class与整数比较的源码,包含了等于、不等于、大于、大于等于、小于、小于等于:

template<typename A, typename B,
	std::enable_if_t<std::is_integral<A>::value || std::is_enum<A>::value> * = nullptr,
	std::enable_if_t<std::is_enum<B>::value || std::is_integral<B>::value> * = nullptr>
bool operator==(A a, B b)
{
    
    
	return a == (A)b;
}

template<typename A, typename B,
	std::enable_if_t<std::is_integral<A>::value || std::is_enum<A>::value> * = nullptr,
	std::enable_if_t<std::is_enum<B>::value || std::is_integral<B>::value> * = nullptr>
bool operator>(A a, B b)
{
    
    
	return a > (A)b;
}

template<typename A, typename B,
	std::enable_if_t<std::is_integral<A>::value || std::is_enum<A>::value> * = nullptr,
	std::enable_if_t<std::is_enum<B>::value || std::is_integral<B>::value> * = nullptr>
bool operator>=(A a, B b)
{
    
    
	return a >= (A)b;
}

template<typename A, typename B,
	std::enable_if_t<std::is_integral<A>::value || std::is_enum<A>::value> * = nullptr,
	std::enable_if_t<std::is_enum<B>::value || std::is_integral<B>::value> * = nullptr>
bool operator<(A a, B b)
{
    
    
	return a < (A)b;
}

template<typename A, typename B,
	std::enable_if_t<std::is_integral<A>::value || std::is_enum<A>::value> * = nullptr,
	std::enable_if_t<std::is_enum<B>::value || std::is_integral<B>::value> * = nullptr>
bool operator<=(A a, B b)
{
    
    
	return a <= (A)b;
}

template<typename A, typename B,
	std::enable_if_t<std::is_integral<A>::value || std::is_enum<A>::value> * = nullptr,
	std::enable_if_t<std::is_enum<B>::value || std::is_integral<B>::value> * = nullptr>
bool operator!=(A a, B b)
{
    
    
	return a != (A)b;
}

这样就可以直接与整数比较了。

Guess you like

Origin blog.csdn.net/witton/article/details/110873292