[英]How do I define / specialize a type_trait in class scope?
我有以下情況:我的問題圍繞使用強類型枚舉類作為標志(就像在C#中使用Flags-Attribute一樣)。 我知道這不是枚舉類首先使用的方式,但這不是這個問題的重點。
我已經定義了幾個要在這些枚舉類上使用的運算符和函數,以及一個自定義類型特征,用於區分常規枚舉和Flag-enums。 這是一個例子:
// 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 };
另請參閱此處了解類似的問題和解決方案。
更新2:自Visual Studio 2013 Update 2以來,當is_flags
特征應用於ios-base標頭時,此解決方案將導致編譯器崩潰。 因此,我們現在使用一種不同的,更清晰的方法,我們使用一個模板類作為enum class
的存儲,並定義所有運算符本身,沒有任何類型特征魔術。 可以使用基礎enum class
隱式創建模板類,使用基礎類型顯式創建模板類。 有魅力,而不是一個enable_if
-mess。
這是一個使用ADL而不是traits的丑陋解決方案(當然你可以在特征中隱藏ADL):
新的運營商模板:
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
更獨特的名稱)
您可以標記枚舉本身:
#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.