gpt4 book ai didi

c++ - 如何在类范围内定义/专门化 type_trait?

转载 作者:可可西里 更新时间:2023-11-01 18:37:49 30 4
gpt4 key购买 nike

我有以下情况:我的问题围绕使用强类型枚举类作为标志(就像在 C# 中使用 Flags-Attribute 一样)。我知道这不是首先要使用枚举类的方式,但这不是这个问题的重点。

我已经定义了几个用于这些枚举类的运算符和函数,以及一个自定义类型特征来区分普通枚举和标志枚举。这是一个例子:

// 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 之外定义特征,这是可能的,但将“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 class Foo { Flags, A = 0x0001, B = 0x0002 };

另见 here对于类似的问题和解决方案。

更新 2:自 Visual Studio 2013 更新 2 起,当 is_flags 特性应用于 ios-base header 时,此解决方案将导致编译器崩溃。因此,我们现在使用一种不同且更简洁的方法,我们使用一个模板类作为 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);
}

关于c++ - 如何在类范围内定义/专门化 type_trait?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21046352/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com