如何为仅影响特定枚举类型的枚举编写通用函数

How to write common functions for enums, that only affect specific enum types?

本文关键字:枚举 函数 类型 影响      更新时间:2023-10-16

我有几个枚举要用作逐位标志。我能在不影响那些我不想被这样对待的人的情况下为所有人编写代码吗?

您可以使用专业化在函数上标记合适的枚举和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;,以便运算符在您使用它的范围内工作。