How do I define / specialize a type_trait in class scope?
c++, enums, flags, type-traits
Solution
You could tag the enumeration 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);
}
Problem
I have the following situation: My problem revolves around using strongly typed enum classes as flags (just as in C# with the Flags-Attribute). I know this is not the way enum classes were meant to be used in the first place, but that it not the point of this question. I have defined several operators and functions to use on these enum classes, and a custom type trait to distinguish normal enums from Flag-enums. Here's 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 above code works fine and using a macro for the template specialization it almost looks like the very convenient C# Flags-Attribute. However, when the `enum class` is not defined in namespace scope, I run into an issue: ``` 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 {}; } ``` The type trait cannot be specialized here. I would need to define the trait outside of X, which is possible, but separates the "Flag-Attribute" from the enum declaration. It would be so nice to use this in our code since flags are used all over the place but in a rather old-fashioned manner (`int` + `#define`). All solutions to this problem I have found so far focus on classes instead of enums, where the solution is much simpler, since I can define the trait as a member of the class itself. Enums, however, cannot inherit, contain typedefs or whatever might be needed to differentiate a certain enum class from another. So is there any possibility to define some kind of trait in a class-scope which can be used in global namespace scope to recognize special enum class-types? EDIT: I should add that I'm using Visual Studio 2013. UPDATE: Thanks for the answers, the tag-solution worked really well, although I had to make a subtle change (making it even more simple in the process). I'm now using this custom 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 need to do is add `Flags` to the enum class, no matter what scope it's in: ``` enum class Foo { Flags, A = 0x0001, B = 0x0002 }; ``` See also here for a similar problem and solution. UPDATE 2: Since Visual Studio 2013 Update 2 this solution will cause compiler crashes when the `is_flags` trait is applied to ios-base headers. Therefore we are now using a different and cleaner approach, we use a template class which acts as the storage for an `enum class` and defines all operators on itself without any type-trait magic. The template class can be created implicit with the underlying `enum class` and explicit with the underlying type. Works a charm and is much less of an `enable_if`-mess.