C++11 class enum: operator& und operator|



  • Hallo,

    C++11 hat ja strongly typed enums, will man sie jedoch als Flags einsetzten, kommt das problem das die operatoren & und | nicht definiert sind.
    Ist das irgendwie elegant lösbar?

    Mein bisheriger ansatz (spoiler: funktioniert nicht)

    template<class enum_class>
    struct enum_op : public enum_class
    {
        friend constexpr enum_op operator|(enum_op& a, enum_op& b)
        {
            return static_cast<enum_op>
                    (static_cast<int>(a) | static_cast<int>(b));
        }
        friend constexpr enum_op operator&(enum_op& a, enum_op& b)
        {
            return static_cast<enum_op>
                    (static_cast<int>(a) & static_cast<int>(b));
        }
    };
    
    enum class enum_type_base {A=1,B=2,C=4};
    typedef enum_op<enum_type_base> enum_type;
    

    Funktioniert nicht. Auch die num selber abzuleiten klappt wohl nicht.
    Ideen?

    phlox81



  • namespace enum_name
    {
        enum enum_name_t : T
        {
            foo, bar, baz
        };
    }
    
    typedef enum_name::enum_name_t enum_name_t;
    

    Ansonsten vielleicht was mit enable_if Operator-Templates und Type-Traits.



  • #include <iostream>
    
    enum class Enum {
    	A = 1,
    	B = 2,
    	C = 4,
    	D = 8
    };
    
    using underlying = std::underlying_type<Enum>::type;
    
    Enum operator|(const Enum& lhs, const Enum& rhs) {
    	return static_cast<Enum>(static_cast<underlying>(lhs) | static_cast<underlying>(rhs));
    }
    Enum operator&(const Enum& lhs, const Enum& rhs) {
    	return static_cast<Enum>(static_cast<underlying>(lhs) & static_cast<underlying>(rhs));
    }
    
    int main(int argc, char** argv)
    {
    	Enum e = Enum::A | Enum::B;
    	std::cout << static_cast<underlying>(e) << std::endl; // 3
    }
    


  • #include <utility>
    #include <type_traits>
    
    template <class T>
    struct has_binary_ops : std::false_type
    {
    };
    
    template <class T>
    constexpr
    typename std::enable_if<has_binary_ops<typename std::decay<T>::type>::value, T>::type
    operator | (T const &left, T const &right)
    {
    	typedef typename std::underlying_type<T>::type underlying;
    	return static_cast<T>(static_cast<underlying>(left) |
    						  static_cast<underlying>(right));
    }
    
    enum class file_flag : unsigned
    {
    	read = 1,
    	write = 2
    };
    
    template <>
    struct has_binary_ops<file_flag> : std::true_type
    {
    };
    
    static_assert(static_cast<unsigned>(file_flag::read | file_flag::write) == 3, "Operator | is expected to work");
    
    int main()
    {
    	//works with L-Values
    	file_flag f = file_flag::read;
    	(f | file_flag::write);
    }
    


  • TyRoXx Lösung ist etwas flexibler, da man schnell für zusätzliche Enums die operatoren aktivieren kann, verfolgt aber das gleiche Prinzip 😃



  • Siehe 17.5.2.1.3 Bitmask types

    typedef int int_type;
    
    enum class bitmask : int_type {a=1<<0, b=1<<1, c=1<<2};
    
    constexpr bitmask operator|(const bitmask& a, const bitmask& b){
      return static_cast<bitmask>(
          static_cast<int_type>(a) | static_cast<int_type>(b));
    }
    
    constexpr bitmask operator&(const bitmask& a, const bitmask& b){
      return static_cast<bitmask>(
          static_cast<int_type>(a) & static_cast<int_type>(b));
    }
    
    constexpr bitmask A(bitmask::a);
    constexpr bitmask B(bitmask::b);
    constexpr bitmask C(bitmask::c);
    
    int main(){
      bitmask b1 = A | B;
      bitmask b2 = A & B;
    }
    

Log in to reply