如何为枚举编写常用函数,只影响特定的枚举类型?

时间:2015-06-25 22:55:34

标签: c++ c++11 templates enums operator-overloading

我有几个枚举我想用作按位标志。我可以为所有这些代码编写代码,而不会影响那些我不想以这种方式对待的代码吗?

2 个答案:

答案 0 :(得分:2)

您可以使用专业化在功能上标记合适的枚举和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) { ... }

答案 1 :(得分:0)

是的,通过使用类型特征和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;,以便运算符可以在您使用它的范围内工作。