如何在类范围内定义/专门化type_trait
How do I define / specialize a type_trait in class scope?
我有以下情况:我的问题围绕着使用强类型枚举类作为标志(就像在带有flags属性的C#中一样)。我知道这不是枚举类最初的使用方式,但这不是这个问题的重点。
我定义了几个运算符和函数用于这些枚举类,并定义了一个自定义类型特征来区分普通枚举和Flag枚举。这里有一个例子:
// 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#标志属性。
然而,当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之外的特性,这是可能的,但将"Flag Attribute"与枚举声明分开。在我们的代码中使用它将是非常好的,因为标志在所有地方都使用,但使用的方式相当老式(int
+#define
)。到目前为止,我发现的这个问题的所有解决方案都集中在类上,而不是枚举上,因为我可以将特性定义为类本身的成员,所以解决方案要简单得多。然而,枚举不能继承、包含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类中,无论它在什么范围:
enum class Foo { Flags, A = 0x0001, B = 0x0002 };
有关类似的问题和解决方案,请参阅此处。
UPDATE 2:自Visual Studio 2013 UPDATE 2以来,当is_flags
特性应用于ios基本标头时,此解决方案将导致编译器崩溃。因此,我们现在使用一种不同的、更干净的方法,我们使用一个模板类作为enum class
的存储,并在没有任何类型特征魔法的情况下定义其自身的所有运算符。模板类可以使用底层enum class
隐式创建,也可以使用底层类型显式创建。工作很有魅力,而且不太像enable_if
。
您可以标记枚举本身:
#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);
}
这里有一个使用ADL而不是traits的丑陋解决方案(当然你可以将ADL隐藏在traits中):
新操作员模板:
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));
};
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;
}
(ADL最好使用比is_flags
更独特的名称)
- 是否可以对零模板参数进行模板专门化
- 使用 [] 运算符时"binding reference of type discards qualifiers"
- 在 QVector<std::unique_ptr 上使用 std::find<Type>>
- 在类型和包装器之间reinterpret_cast是否安全<Type>?
- 使用 cmake 的 Linux 终端上的"Conversion to non-scalar type is requested"错误
- 控制到达非空函数clang(-Wreturn-type)的末尾
- 尝试根据类中 typedef 的存在来专门化模板函数
- 如何基于模板化类的基类专门化成员函数
- std::unordered_map 类型对象声明期间出现"field has incomplete type"错误
- 将系数存储在头文件的数组中("does not name a type"错误)
- 尝试打开 ifstream 时出现"Incomplete type"错误
- 将成员函数作为构造函数参数调用时出错 "Variable is not a type name"
- 在"结构提升::enable_if<提升::is_pod<T>,无效>"中没有名为"type"的类型
- 如何为指向复杂值的迭代器专门化算法?
- "Missing type specifier - int assumed"无法通过向主函数添加"return 0"来解决
- OpenCV CV_16F type
- 专门化模板覆盖函数/避免对象切片
- 我能否根据其运算符()的签名专门化可变参数模板参数
- C++ "错误:在'类 std::result_of< ... >"中没有名为'type'的类型"
- 错误 C2893 无法专门化函数模板'unknown-type std::invoke(_Callable &&,_Types &&...)'