[英]How do I define / specialize a type_trait in class scope?
I have the following situation: My problem revolves around using strongly typed enum classes as flags (just as in C# with the Flags-Attribute). 我有以下情况:我的问题围绕使用强类型枚举类作为标志(就像在C#中使用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. 我已经定义了几个要在这些枚举类上使用的运算符和函数,以及一个自定义类型特征,用于区分常规枚举和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
我可以执行以下操作:
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. 上面的代码工作正常,并使用宏进行模板专业化,它几乎看起来像非常方便的C#Flags-Attribute。
However, when the enum class
is not defined in namespace scope, I run into an issue: 但是,当在命名空间范围内未定义
enum class
时,我遇到了一个问题:
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.
我需要在X之外定义特征,这是可能的,但是将“Flag-Attribute”与枚举声明分开。 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
). 在我们的代码中使用它会很好,因为标志遍布整个地方但是以一种相当老式的方式(
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.
但是,枚举不能继承,包含typedef或者将某个枚举类与另一个枚举类区分开来可能需要的任何东西。
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. 编辑:我应该补充说我正在使用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: 现在,我需要做的就是添加
Flags
的枚举类,不管它是在什么范围:
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. 更新2:自Visual Studio 2013 Update 2以来,当
is_flags
特征应用于ios-base标头时,此解决方案将导致编译器崩溃。 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. 因此,我们现在使用一种不同的,更清晰的方法,我们使用一个模板类作为
enum class
的存储,并定义所有运算符本身,没有任何类型特征魔术。 The template class can be created implicit with the underlying enum class
and explicit with the underlying type. 可以使用基础
enum class
隐式创建模板类,使用基础类型显式创建模板类。 Works a charm and is much less of an enable_if
-mess. 有魅力,而不是一个
enable_if
-mess。
Here's an ugly solution using ADL instead of traits (of course you can hide the ADL inside the trait): 这是一个使用ADL而不是traits的丑陋解决方案(当然你可以在特征中隐藏ADL):
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));
};
Definition of is_flags
for Bar
: Bar
的is_flags
定义:
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, use a more unique name than is_flags
for ADL) (最好使用比ADL的
is_flags
更独特的名称)
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);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.