【发布时间】:2014-01-10 14:06:04
【问题描述】:
我有以下情况: 我的问题围绕着使用强类型枚举类作为标志(就像在带有标志属性的 C# 中一样)。我知道这不是 enum 类最初的使用方式,但这不是这个问题的重点。
我已经定义了几个用于这些枚举类的运算符和函数,以及一个自定义类型特征来区分普通枚举和标志枚举。这是一个例子:
// 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));
};
现在,如果我定义任何 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
}
上面的代码工作正常,并且使用宏来进行模板特化,它看起来就像非常方便的 C# Flags-Attribute。
但是,当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 {};
}
类型特征不能在这里特化。我需要在 X 之外定义特征,这是可能的,但将“标志属性”与枚举声明分开。在我们的代码中使用它会非常好,因为标志在所有地方都使用但以一种相当老式的方式(int + #define)。到目前为止,我发现这个问题的所有解决方案都集中在类而不是枚举上,其中解决方案要简单得多,因为我可以将 trait 定义为类本身的成员。然而,枚举不能继承、包含 typedef 或任何可能需要将某个枚举类与另一个区分的东西。
那么有没有可能在类作用域中定义某种特征,它可以在全局命名空间作用域中用于识别特殊的枚举类类型?
编辑:我应该补充一点,我正在使用 Visual Studio 2013。
更新:感谢您的回答,标签解决方案工作得非常好,尽管我必须做出细微的改变(让它在过程中变得更加简单)。我现在正在使用这个自定义类型特征:
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;
};
现在,我需要做的就是将Flags 添加到枚举类中,无论它在什么范围内:
enum class Foo { Flags, A = 0x0001, B = 0x0002 };
有关类似问题和解决方案,另请参阅 here。
更新 2: 自 Visual Studio 2013 更新 2 起,当 is_flags 特征应用于 ios-base 标头时,此解决方案将导致编译器崩溃。因此,我们现在使用一种不同且更简洁的方法,我们使用一个模板类作为enum class 的存储,并在其自身上定义所有运算符,而无需任何类型特征魔法。模板类可以用底层enum class 隐式创建,也可以用底层类型显式创建。很有魅力,不像enable_if-mess。
【问题讨论】:
-
您可以使用
constexpr函数和 ADL 而不是特征。它可能很难看,但允许在struct X内定义friend函数。 -
@dyp 听起来不错,但不幸的是我使用的是 VS2013,它还不支持
constexpr。我将编译器添加到问题中。 -
那么您仍然可以使用
std::true_type和std::false_type(或者更好的是:唯一类型)和decltype来调用ADL。
标签: c++ enums flags typetraits