How to define / specialize type_trait scope in a class?

I have the following situation: My problem revolves around using strongly typed enum classes as flags (as in C # with the Flags-Attribute attribute). I know that this is not how the enum classes should have been used in the first place, but that is not the question of this question.

I have defined several operators and functions for use in these enum classes and a special type to distinguish normal enums from flag enums. Here is an example:

// Default type_trait which disables the following operators template <typename T> struct is_flags : std::false_type {}; // Example operator to use enum class as flags template <typename T> std::enable_if_t<std::is_enum<T>::value && is_flags<T>::value, T&> operator|=(T &t1, const T t2) { return t1 = static_cast<T>(static_cast<std::underlying_type_t<T>>(t1) | static_cast<std::underlying_type_t<T>>(t2)); }; 

Now, if I define any enum class , I can do the following:

 enum class Foo { A = 1, B = 2 }; enum class Bar { A = 1, B = 2 }; // Declare "Bar" to be useable like Flags template <> struct is_flags<Bar> : std::true_type {}; void test() { Foo f; Bar b; f |= Foo::A; // Doesn't compile, no operator |= b |= Bar::A; // Compiles, type_trait enables the operator } 

The code above works fine and using a macro to specialize a template it almost looks like a very convenient C # Flags-Attribute.

However, when the enum class not defined in the namespace scope, I ran into a problem:

 struct X { enum class Bar { A = 1, B = 2 }; // Following line gives: C3412: Cannot specialize template in current scope template <> struct is_flags<Bar> : std::true_type {}; } 

A typical feature here cannot be specialized. I would need to define a tag outside of X, which is possible, but it separates the "flag attribute" from the enumeration declaration. It would be so nice to use this in our code, since flags are used everywhere, but rather old-fashioned ( int + #define ). All the solutions to this problem that I have found so far are focused on classes instead of enumerations, where the solution is much simpler, since I can define the trait as a member of the class itself. However, enumerations cannot inherit, contain typedefs or anything else that may be required to differentiate a particular enum class from another.

So, is it possible to define some attribute in the class field that can be used in the global namespace to recognize special types of enum classes?

EDIT: I have to add that I am using Visual Studio 2013.

UPDATE: Thanks for the answers, the tag solution worked very well, although I had to make a subtle change (making it even simpler in the process). Now I use this special type trait:

 template <typename T> struct is_flags { private: template <typename U> static std::true_type check(decltype(U::Flags)*); template <typename> static std::false_type check(...); typedef decltype(check<T>(0)) result; public: static const bool value = std::is_enum<T>::value && result::value; }; 

Now all I have to do is add Flags to the enumeration class, no matter what scope it is in:

 enum class Foo { Flags, A = 0x0001, B = 0x0002 }; 

See also here for a similar problem and solution.

UPDATE 2: Starting with Visual Studio 2013 Update 2, this solution will cause compiler crashes if the is_flags trait is_flags applied to ios-base headers. Therefore, we now use a different and cleaner approach, we use a template class that acts as a repository for the enum class and defines all the operators on itself without title magic. A template class can be created implicit with the base enum class and explicit with the base type. Charm works and a lot less enable_if .

+6
source share
2 answers

You can mark the listing itself:

 #include <type_traits> template<typename T> struct is_flags { private: typedef typename std::underlying_type<T>::type integral; template<integral> struct Wrap {}; template<typename U> static constexpr std::true_type check(Wrap<integral(U::EnumFlags)>*); template<typename> static constexpr std::false_type check(...); typedef decltype(check<T>(0)) result; public: static constexpr bool value = std::is_enum<T>::value && result::value; }; namespace Detail { template <bool> struct Evaluate; template <> struct Evaluate<true> { template <typename T> static T apply(T a, T b) { return T(); } }; } template <typename T> T evalueate(T a, T b) { return Detail::Evaluate<is_flags<T>::value>::apply(a, b); } enum class E{ A = 1, B, C }; struct X { enum class F{ EnumFlags, A = 1, B, C }; }; int main () { // error: incomplete type 'Detail::Evaluate<false>' used in nested name specifier // evalueate(E::A, E::B); evalueate(X::F::A, X::F::B); } 
+2
source

Here's an ugly solution using ADL instead of signs (of course, you can hide the ADL inside the slash):

New operator template:

 struct my_unique_enum_flag_type; // Example operator to use enum class as flags template <typename T> enable_if_t<std::is_enum<T>::value && std::is_same<decltype(is_flags(std::declval<T>())), my_unique_enum_flag_type>::value, T&> operator|=(T &t1, const T t2) { return t1 = static_cast<T>(static_cast<underlying_type_t<T>>(t1) | static_cast<underlying_type_t<T>>(t2)); }; 

The definition of is_flags for Bar :

 struct X { enum class Bar { A = 1, B = 2 }; friend my_unique_enum_flag_type is_flags(Bar); }; int main() { X::Bar a = X::Bar::A; a |= X::Bar::B; } 

(preferably using a more unique name than is_flags for ADL)

+2
source

All Articles