如何为仅影响特定枚举类型的枚举编写通用函数
How to write common functions for enums, that only affect specific enum types?
我有几个枚举要用作逐位标志。我能在不影响那些我不想被这样对待的人的情况下为所有人编写代码吗?
您可以使用专业化在函数上标记合适的枚举和SFINAE:
template<typename E> struct is_bitwise_enum : std::false_type {};
现在这样标记所有的逐位枚举:
template<> struct is_bitwise_enum< my_enum > : std::true_type {};
template<> struct is_bitwise_enum< my_other_enum > : std::true_type {};
并保护这样的功能:
template<typename E>
typename std::enable_if<is_bitwise_enum<E>::value,E>::type
operator|(const E lhs, const E rhs) { ... }
是的,通过使用类型特征和SFINAE,您可以指定函数/运算符是否可以与枚举一起使用。
已编辑
我完成了我的头文件,并决定使用Daniel Frey描述的专业化思想进行共享,以防有人想要它或想在它的基础上进行构建
#ifdef _MSC_VER
#pragma once // For VC++
#endif
#ifndef ENUM_FUNCTIONS_H
#define ENUM_FUNCTIONS_H
///////////////////////////////////////////////////////////////////////////////
// Common enum functions so that the same code doesn't have to be written over
// and over again for different enums.
//
// -- Written by: Adrian Hawryluk, June 2015
///////////////////////////////////////////////////////////////////////////////
#include <type_traits>
namespace enums
{
size_t const version = 1;
// If your compiler doesn't understand constexpr, then set CONSTEXPR to blank,
// otherwise set to constexpr.
#define CONSTEXPR //constexpr
///////////////////////////////////////////////////////////////////////////
// Use Enums as Bit Flags
//
// These functions allow the treating of an enum as a set of bits with
// which you can use a subset of the standard bitwise operators:
//
// |, |=, &, &=, ^, ^=
//
// and the functions:
//
// Cast, CastRef, CastSigned, CastSignedRef, CastUnsigned, CastUnsignedRef,
// MakeSet, Set, MakeClear, Clear, MakeToggle, Toggle, AreAllSet,
// AreAnySet, AreAllClear, AreAnyClear, FirstSet, FirstCleared
//
// To enable the use of these operators and functions, define:
//
// namespace enums {
// template<> struct is_bitflag_enum<my_enum> : std::true_type {};
// }
//
// where my_enum is the enum being defined to use these functions.
//
// To be able to use the operators, you need to state that you are using the
// namespace in the appropriate scope:
//
// using namespace enums;
template<typename E> struct is_bitflag_enum : std::false_type {};
// operator ^=
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E&>::type
operator^=(E& lhs, E rhs)
{
typedef typename std::make_unsigned<typename std::underlying_type<E>::type>::type uint_t;
return (E&)((uint_t&)lhs ^= (uint_t)rhs);
}
// operator ~
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E>::type
operator~(E rhs)
{
typedef typename std::make_unsigned<typename std::underlying_type<E>::type>::type uint_t;
return (E)(~(uint_t)rhs);
}
// operator |=
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E&>::type
operator|=(E& lhs, E rhs)
{
typedef typename std::make_unsigned<typename std::underlying_type<E>::type>::type uint_t;
return (E&)((uint_t&)lhs |= (uint_t)rhs);
}
// operator |
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E>::type
operator|(E lhs, E rhs)
{
typedef typename std::make_unsigned<typename std::underlying_type<E>::type>::type uint_t;
return (E)((uint_t)lhs | (uint_t)rhs);
}
// operator &=
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E&>::type
operator&=(E& lhs, E rhs)
{
typedef typename std::make_unsigned<typename std::underlying_type<E>::type>::type uint_t;
return (E&)((uint_t&)lhs &= (uint_t)rhs);
}
// operator &
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E>::type
operator&(E lhs, E rhs)
{
typedef typename std::make_unsigned<typename std::underlying_type<E>::type>::type uint_t;
return (E)((uint_t)lhs & (uint_t)rhs);
}
// MakeSet(initialFlagState, flagModification)
//
// Sets the on bits in flagModification to on in initialFlagState.
// initialFlagState is NOT modified, but returs a new value
// (i.e. "Makes" a new value).
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E>::type
MakeSet(E initialFlagState, E flagModification)
{
return initialFlagState | flagModification;
}
// Set(initialFlagState, flagModification)
//
// Sets the on bits in flagModification to on in initialFlagState.
// initialFlagState is modified, and returs a reference to that variable.
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E&>::type
Set(E& initialFlagState, E flagModification)
{
return initialFlagState |= flagModification;
}
// MakeClear(initialFlagState, flagModification)
//
// Clears the on bits in flagModification to off in initialFlagState.
// initialFlagState is NOT modified, but returs a new value
// (i.e. "Makes" a new value).
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E>::type
MakeClear(E initialFlagState, E flagModification)
{
return initialFlagState & ~flagModification;
}
// Clear(initialFlagState, flagModification)
//
// Clears the on bits in flagModification to off in initialFlagState.
// initialFlagState is modified, and returs a reference to that variable.
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E&>::type Clear(E& initialFlagState, E flagModification)
{
return initialFlagState &= ~flagModification;
}
// MakeToggle(initialFlagState, flagModification)
//
// Toggles the on bits in flagModification in initialFlagState.
// initialFlagState is NOT modified, but returs a new value
// (i.e. "Makes" a new value).
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E>::type
MakeToggle(E initialFlagState, E flagModification)
{
return initialFlagState ^ flagModification;
}
// Toggle(initialFlagState, flagModification)
//
// Toggles the on bits in flagModification in initialFlagState.
// initialFlagState is modified, and returs a reference to that variable.
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E&>::type
Toggle(E& initialFlagState, E flagModification)
{
return initialFlagState ^= flagModification;
}
// AreAllSet(flagState, flagsToCompare)
//
// Returns true if all bits in flagToCompare are set in flagState,
// false otherwise.
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
bool>::type
AreAllSet(E flagState, E flagsToCompare)
{
return (flagState & flagsToCompare) == flagsToCompare;
}
// AreAnySet(flagState, flagsToCompare)
//
// Returns true if any bits in flagToCompare are set in flagState,
// false otherwise.
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
bool>::type
AreAnySet(E flagState, E flagsToCompare)
{
return (flagState & flagsToCompare) != (E)0;
}
// AreAllClear(flagState, flagsToCompare)
//
// Returns true if all bits in flagToCompare are cleared in flagState,
// false otherwise.
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
bool>::type
AreAllClear(E flagState, E flagsToCompare)
{
return (~flagState & flagsToCompare) == flagsToCompare;
}
// AreAnyClear(flagState, flagsToCompare)
//
// Returns true if any bits in flagToCompare are cleared in flagState,
// false otherwise.
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
bool>::type
AreAnyClear(E flagState, E flagsToCompare)
{
return (~flagState & flagsToCompare) != (E)0;
}
// FirstSet(flagState, flagToCompare)
//
// Return the first flag in the list that is set in flagState.
// If all are not set, return (E)0.
//
// NOTE: If a flag in the list is (E)0, then it will ignore that element
// in the list, and continue to the next one.
// NOTE: If a flag in the list represents 2 or more bits, only a subset of
// those bits need be set to result in the returned value.
template<typename E, typename ...Es>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E>::type
FirstSet(E flagState, E flagToCompare)
{
if (AreAnySet(flagState, flagToCompare))
{
return flagToCompare;
}
return (E)0;
}
// FirstSet(flagState, flagToCompare, ...)
//
// Return the first flag in the list that is set in flagState.
// If all are not set, return (E)0.
//
// NOTE: If a flag in the list is (E)0, then it will ignore that element
// in the list, and continue to the next one.
// NOTE: If a flag in the list represents 2 or more bits, only a subset of
// those bits need be set to result in the returned value.
template<typename E, typename ...Es>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E>::type
FirstSet(E flagState, E flagToCompare, Es...flagsToCompare)
{
if (AreAnySet(flagState, flagToCompare))
{
return flagToCompare;
}
return FirstSet(flagState, flagsToCompare...);
}
// FirstCleared(flagState, flagToCompare)
//
// Return the first flag in the list that is NOT set in flagState.
// If all are not set, return (E)0.
//
// NOTE: If a flag in the list is (E)0, then it will not go beyond that
// element in the list.
// NOTE: If a flag in the list represents 2 or more bits, only a subset of
// those bits need be cleared to result in the returned value.
template<typename E, typename ...Es>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E>::type
FirstCleared(E flagState, E flagToCompare)
{
if (AreAnyClear(flagState, flagToCompare))
{
return (E)0;
}
return flagToCompare;
}
// FirstCleared(flagState, flagToCompare, ...)
//
// Return the first flag in the list that is cleared in flagState.
// If all are cleared, return (E)0.
//
// NOTE: If a flag in the list is (E)0, then it will not go beyond that
// element in the list.
// NOTE: If a flag in the list represents 2 or more bits, only a subset of
// those bits need be cleared to result in the returned value.
template<typename E, typename ...Es>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value,
E>::type
FirstCleared(E flagState, E flagToCompare, Es...flagsToCompare)
{
if (AreAnyClear(flagState, flagToCompare))
{
return FirstCleared(flagState, flagsToCompare...);
}
return flagToCompare;
}
///////////////////////////////////////////////////////////////////////////
// Use With Shift Operators
//
// These functions allow the treating of an enum as a siftable set of bits
// with which you can use a subset of the standard bitwise operators:
//
// <<, <<=, >>, >>=
//
// and the functions:
//
// Cast, CastRef, CastSigned, CastSignedRef, CastUnsigned, CastUnsignedRef
//
// To enable the use of these operators and functions, define:
//
// namespace enums {
// template<> struct is_shiftable_enum<my_enum> : std::true_type {};
// }
//
// where my_enum is the enum being defined to use these functions.
//
// To be able to use the operators, you need to state that you are using the
// namespace in the appropriate scope:
//
// using namespace enums;
template<typename E> struct is_shiftable_enum : std::false_type {};
// operator <<
template<typename E>
CONSTEXPR typename std::enable_if<is_shiftable_enum<E>::value,
E>::type
operator<<(E lhs, int rhs)
{
typedef typename std::make_unsigned<typename std::underlying_type<E>::type>::type uint_t;
return (E)(((uint_t)lhs) << rhs);
}
// operator <<=
template<typename E>
CONSTEXPR typename std::enable_if<is_shiftable_enum<E>::value,
E&>::type
operator<<=(E& lhs, int rhs)
{
typedef typename std::make_unsigned<typename std::underlying_type<E>::type>::type uint_t;
return (E&)(((uint_t&)lhs) <<= rhs);
}
// operator >>
template<typename E>
CONSTEXPR typename std::enable_if<is_shiftable_enum<E>::value,
E>::type
operator>>(E lhs, int rhs)
{
typedef typename std::make_unsigned<typename std::underlying_type<E>::type>::type uint_t;
return (E)(((uint_t)lhs) << rhs);
}
// operator >>=
template<typename E>
CONSTEXPR typename std::enable_if<is_shiftable_enum<E>::value,
E&>::type
operator>>=(E& lhs, int rhs)
{
typedef typename std::make_unsigned<typename std::underlying_type<E>::type>::type uint_t;
return (E&)(((uint_t&)lhs) <<= rhs);
}
///////////////////////////////////////////////////////////////////////////
// Casting functions
//
// These functions are to cast the enum to the underlying type, some of
// which allowing to coerce the type to be signed or not.
// Cast enum to the underlying type reference (lvalue).
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value || is_shiftable_enum<E>::value,
typename std::underlying_type<E>::type>::type&
CastRef(E& flagState)
{
return (decltype(CastRef(flagState)))flagState;
}
// Cast enum to a signed integer reference (lvalue).
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value || is_shiftable_enum<E>::value,
typename std::make_signed<typename std::underlying_type<E>::type>::type>::type&
CastSignedRef(E& flagState)
{
return (decltype(CastSignedRef(flagState)))flagState;
}
// Cast enum to an unsigned integer reference (lvalue).
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value || is_shiftable_enum<E>::value,
typename std::make_unsigned<typename std::underlying_type<E>::type>::type>::type&
CastUnsignedRef(E& flagState)
{
return (decltype(CastUnsignedRef(flagState)))flagState;
}
// Cast enum to the underlying type value (rvalue).
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value || is_shiftable_enum<E>::value,
typename std::underlying_type<E>::type>::type
Cast(E flagState)
{
return (decltype(Cast(flagState)))flagState;
}
// Cast enum to a signed integerr value (rvalue).
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value || is_shiftable_enum<E>::value,
typename std::make_signed<typename std::underlying_type<E>::type>::type>::type
CastSigned(E flagState)
{
return (decltype(CastSigned(flagState)))flagState;
}
// Cast enum to an unsigned integerr value (rvalue).
template<typename E>
CONSTEXPR typename std::enable_if<is_bitflag_enum<E>::value || is_shiftable_enum<E>::value,
typename std::make_unsigned<typename std::underlying_type<E>::type>::type>::type
CastUnsigned(E flagState)
{
return (decltype(CastUnsigned(flagState)))flagState;
}
#undef CONSTEXPR
}
#endif // ENUM_FUNCTIONS_H
在包含的代码中,枚举将通过指定来访问这些函数
namespace enums {
template<> struct is_shiftable_enum<my_enum> : std::true_type {};
}
其中my_enum
是要授予访问权限的枚举。使用运算符时,必须指定using namespace enums;
,以便运算符在您使用它的范围内工作。
相关文章:
- C++中构造函数中的枚举
- 是否有 Windows 用户空间函数来枚举连接的网络共享?
- 如何使用默认值为构造函数中的枚举赋值?
- 泛型枚举和其他类型的重载模板函数
- 在运行时使用枚举器值作为模板函数的模板参数的元程序
- 函数-本地枚举声明和 ADL 的交互
- 如何从枚举类值中指定模板函数参数中的数组大小?
- 是否可以使用泛型枚举类型作为函数的参数?
- 枚举模板函数一般调用
- 为什么C++不为枚举类型提供默认"operator>>"函数?
- 从枚举中选择适当的函数
- 如何为返回枚举元组的 C++ 函数编写 cython 包装器?
- 如何将带有"any"枚举的 std::map 传递给函数
- 将函数参数限制为某些枚举值
- C++ 成员函数的多个定义,基于枚举模板参数
- 类中的枚举在调用自己的函数时不会改变
- 将全球枚举的全局枚举更改为函数中的int值
- 枚举值依赖函数调用
- 是否可以将非枚举值作为枚举函数参数传递
- C++中的枚举 - 函数如何分配值