2014-04-17 35 views
1

使用以下模板來嘗試使C++ 11/14的新類類枚舉按需工作,我發現下面的代碼甚至不會嘗試調用隱式ctor以使用將適合通過VS2013更新1非成員模板:爲什麼不是我的隱式ctor在下面調用

BitTest定義爲:

template <typename enumT> 
bool BitTest(const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs) 
{ 
    return (lhs & rhs); 
} 

測試代碼與異常:

enum class Flags { None = 0x00, CanChangeDataSources = 0x01, RequiresExclusiveAccess = 0x02 }; 
EnumeratedFlags<Flags> lhs(Flags::CanChangeDataSources); 

// ... the following fails to even attempt to convert the Foo::Flags 
if (BitTest(lhs, Flags::CanChangeDataSources)) { DoSomething(); } 

// this compiles, but I don't know why it's necessary (and this is annoying and ugly)... 
if (BitTest(lhs, EnumeratedFlags<Flags>(Flags::CanChangeDataSources))) { DoSomething(); } 

這裏是模板defini重刑我目前嘗試使用:

template <typename enumT> 
class EnumeratedFlags 
{ 
public: 

    typedef enumT enum_type; 
    typedef typename std::underlying_type<enumT>::type store_type; 

// constructors 

    EnumeratedFlags() 
     : m_bits(0) 
    { 
    } 

    EnumeratedFlags(enum_type flag) 
     : m_bits(static_cast<store_type>(flag)) 
    { 
    } 

    explicit EnumeratedFlags(store_type value) 
     : m_bits(value) 
    { 
    } 

    EnumeratedFlags(const std::initializer_list<enum_type> & initializers) 
     : m_bits(0) 
    { 
     for (auto flag : initializers) 
      m_bits |= static_cast<store_type>(flag); 
    } 

// operators 

    operator std::string() const 
    { 
     return to_string(); 
    } 

    bool operator [] (enum_type flag) const 
    { 
     return test(flag); 
    } 

    store_type operator *() const 
    { 
     return m_bits; 
    } 

    operator bool() const 
    { 
     return m_bits != store_type(0); 
    } 

// explicit accessors 

    store_type bits() const 
    { 
     return m_bits; 
    } 

    std::string to_string() const 
    { 
     std::string str(size(), '0'); 

     for (size_t x = 0; x < size(); ++x) 
      str[size() - x - 1] = (m_bits & (1 << x) ? '1' : '0'); 

     return str; 
    } 

    EnumeratedFlags & set(enum_type flag) 
    { 
     BitSet(m_bits, static_cast<store_type>(flag)); 
     return *this; 
    } 

    EnumeratedFlags & set_if(enum_type flag, bool set_or_clear) 
    { 
     BitSetIf(m_bits, static_cast<store_type>(flag), set_or_clear); 
     return *this; 
    } 

    EnumeratedFlags & clear() 
    { 
     m_bits = store_type(0); 
     return *this; 
    } 

    EnumeratedFlags & flip() 
    { 
     m_bits = ~m_bits; 
     return *this; 
    } 

    EnumeratedFlags & flip(enum_type flag) 
    { 
     m_bits ^= static_cast<store_type>(flag); 
     return *this; 
    } 

    size_t count() const 
    { 
     // http://www-graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan 

     store_type bits = m_bits; 
     size_t total = 0; 
     for (; bits != 0; ++total) 
     { 
      bits &= bits - 1; // clear the least significant bit set 
     } 
     return total; 
    } 

    size_t size() const 
    { 
     // one character per possible bit 
     return sizeof(enum_type) * 8; 
    } 

    bool test(enum_type flag) const 
    { 
     return BitTest(m_bits, static_cast<store_type>(flag)); 
    } 

    bool any() const 
    { 
     return m_bits != 0; 
    } 

    bool none() const 
    { 
     return m_bits == 0; 
    } 

private: 

    store_type m_bits; 
}; 

template <class charT, class traits, typename enumT> 
std::basic_ostream<charT, traits> & operator << (std::basic_ostream<charT, traits> & os, const EnumeratedFlags<enumT> & flags) 
{ 
    return os << flags.to_string(); 
} 

template <typename enumT> 
EnumeratedFlags<enumT> operator & (const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs) 
{ 
    return EnumeratedFlags<enumT>(lhs.bits() & rhs.bits()); 
} 

template <typename enumT> 
EnumeratedFlags<enumT> operator | (const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs) 
{ 
    return EnumeratedFlags<enumT>(lhs.bits() | rhs.bits()); 
} 

template <typename enumT> 
EnumeratedFlags<enumT> operator^(const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs) 
{ 
    return EnumeratedFlags<enumT>(lhs.bits()^rhs.bits()); 
} 

template <typename enumT> 
bool BitTest(const EnumeratedFlags<enumT>& lhs, const EnumeratedFlags<enumT>& rhs) 
{ 
    return (lhs & rhs); 
} 

基本上,我本來以爲在X (const T & lhs, const T & rhs)形式的任何自由函數將最多使用一個用戶自定義轉換找到BitTest<>()有效的調用,上面。相反,我必須在上面的代碼中明確地聲明轉換,以使編譯器使用此函數,這大大降低了template class EnumeratedFlags<>的表達能力。一般來說,C++迫使我堅持認爲,沒有一種好的方法來使用位,它結合了使用範圍枚舉(enum class Foo)和位域(或類似命名集合)的所有功能和良好的編程習慣),並且在保持編譯器基本的完整性檢查(不會自動轉換爲數字類型或反之)的同時,使客戶端程序員非常容易地使用它們。似乎需要在語言規範中進行更全面的改進,才能實現真正照亮的位域枚舉......或者我錯過了什麼?

+3

當模板類型扣參與,沒有用戶定義的轉換被考慮。 – dyp

+1

相關:http://stackoverflow.com/q/18553843/420683 – dyp

+0

你在BitTest中要求一個'EnumeratedFlags'類型,但是你不會在第一個if中爲'rhs'參數傳遞'EnumeratedFlags'類型聲明。不知道'enum'應該如何隱式轉換爲'EnumeratedFlags'。 – Brian

回答

5

§14.8.1/ 6 [temp.arg.explicit]

(第4章)將在一個函數的參數來執行隱式轉換將其轉換爲相應的功能參數的類型如果參數類型不包含參與模板參數推導的模板參數

在你的榜樣的第二個參數BitTest()不參與模板參數推導,因此沒有隱式轉換嘗試和編譯器是無法Flags的類型轉換爲const EnumeratedFlags<Flag>&

您可以通過將第二個參數類型轉換爲非推導上下文來解決此問題,從而阻止它參與模板參數推演。

template <typename enumT> 
bool BitTest(const EnumeratedFlags<enumT>& lhs, 
      const EnumeratedFlags<typename EnumeratedFlags<enumT>::enum_type>& rhs) 
{ 
    return (lhs & rhs); 
} 

Live demo

當然,其他的解決方案是提供這些重載代替

template <typename enumT> 
bool BitTest(const EnumeratedFlags<enumT>& lhs, 
      enumT rhs) 
{ 
    return (lhs & EnumeratedFlags<enumT>(rhs)); 
} 

template <typename enumT> 
bool BitTest(enumT lhs, 
      const EnumeratedFlags<enumT>& rhs) 
{ 
    return BitTest(rhs, lhs); 
} 
+0

這很酷。我當然可以明確定義TxT,TxU,UxT(其中T是EnumeratedFlags )的各種邏輯運算符(以及不等於,不等於)。這是囉嗦(但可行),但留下我沒有一個優雅的解決方案,如何寫UxU操作(他們真的應該導致EnumeratedFlags )。我不能僅僅在一個完全不合格的U上定義這樣的操作,或者這些函數將成爲每種嘗試在所考慮的每種類型上使用這些邏輯操作符的候選者::( – Mordachai

+0

@Mordachai你可以使用'enable_if'和'is_enum'來約束這樣一個函數模板 – Praetorian

+0

我擔心我會需要一些方法來選擇,而不僅僅是查看is_enum。由於爲所有枚舉創建這樣的函數肯定會破壞現有的代碼,所以我需要一些機制來說' enable_if >'...... – Mordachai

1

我的問題是上面已經回答了。然而,這一直困擾着我,所以我能夠充分利用每個人的反應,並提出了我認爲是第一次的機制,我真的很喜歡!

這給了我一種使用C++枚舉來存儲位標誌值的方法,然後在邏輯上對它們進行位操作,同時從不丟失類型信息或編譯器的幫助以確保我陷入成功的陷阱。:)

我希望這也能幫助你! (注:此代碼編譯和下VS2013更新1正常運行)

#pragma once 

#include <type_traits> 

// This is my ongoing attempt to make a really solid enumeration facility in C++11/14 
// 
// What I hate about C++98 (and older) enum 
// - lack of namespace scoping of the non-class enum (name collisions everywhere) 
// - auto conversion to numeric types (int i = MyEnumConstant), but no conversion back again (so supports losing info, but restricts regaining it) 
// 
// What I hate about C++11/14 built-in `enum class X` 
// - having to constantly cast in order to treat them (now neither direction works) 
// - no built-in mechanism to treat enumerated values as bits or bit-flags 

template <typename enum_type> 
class bitflag_enum 
{ 
public: 

    // expose our underlying types 
    typedef enum_type enum_type; 
    typedef typename std::underlying_type<enum_type>::type store_type; 

    // constructors 

    bitflag_enum() 
     : m_bits(0) 
    { 
    } 

    bitflag_enum(enum_type flag) 
     : m_bits(static_cast<store_type>(flag)) 
    { 
    } 

    explicit bitflag_enum(store_type value) 
     : m_bits(value) 
    { 
    } 

    // operators 

    operator bool() const 
    { 
     return m_bits != store_type(0); 
    } 

    // explicit accessors 

    store_type bits() const 
    { 
     return m_bits; 
    } 

private: 

    store_type m_bits; 
}; 

// because implicit conversion isn't considered if a type participates in template type deduction, 
// we've defined both homogeneous and heterogeneous operators here for bitflag_enum<enum_type> and enum_type 
// hence we define logical operators &, |,^and comparisons for TxT, TxU, UxT (where T is bitflag_enum<enum_type>, and U is enum_type) 

template <typename enum_type> 
bool operator != (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs) 
{ 
    return bitflag_enum<enum_type>(lhs.bits() != rhs.bits()); 
} 

template <typename enum_type> 
bool operator != (bitflag_enum<enum_type> lhs, enum_type rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(lhs.bits() != static_cast<store_type>(rhs)); 
} 

template <typename enum_type> 
bool operator != (enum_type lhs, bitflag_enum<enum_type> rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) != rhs.bits()); 
} 

template <typename enum_type> 
bool operator == (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs) 
{ 
    return bitflag_enum<enum_type>(lhs.bits() == rhs.bits()); 
} 

template <typename enum_type> 
bool operator == (bitflag_enum<enum_type> lhs, enum_type rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(lhs.bits() == static_cast<store_type>(rhs)); 
} 

template <typename enum_type> 
bool operator == (enum_type lhs, bitflag_enum<enum_type> rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) == rhs.bits()); 
} 

template <typename enum_type> 
bitflag_enum<enum_type> operator & (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs) 
{ 
    return bitflag_enum<enum_type>(lhs.bits() & rhs.bits()); 
} 

template <typename enum_type> 
bitflag_enum<enum_type> operator & (bitflag_enum<enum_type> lhs, enum_type rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(lhs.bits() & static_cast<store_type>(rhs)); 
} 

template <typename enum_type> 
bitflag_enum<enum_type> operator & (enum_type lhs, bitflag_enum<enum_type> rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs)& rhs.bits()); 
} 

template <typename enum_type> 
bitflag_enum<enum_type> operator | (bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs) 
{ 
    return bitflag_enum<enum_type>(lhs.bits() | rhs.bits()); 
} 

template <typename enum_type> 
bitflag_enum<enum_type> operator | (bitflag_enum<enum_type> lhs, enum_type rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(lhs.bits() | static_cast<store_type>(rhs)); 
} 

template <typename enum_type> 
bitflag_enum<enum_type> operator | (enum_type lhs, bitflag_enum<enum_type> rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) | rhs.bits()); 
} 

template <typename enum_type> 
bitflag_enum<enum_type> operator^(bitflag_enum<enum_type> lhs, bitflag_enum<enum_type> rhs) 
{ 
    return bitflag_enum<enum_type>(lhs.bits()^rhs.bits()); 
} 

template <typename enum_type> 
bitflag_enum<enum_type> operator^(bitflag_enum<enum_type> lhs, enum_type rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(lhs.bits()^static_cast<store_type>(rhs)); 
} 

template <typename enum_type> 
bitflag_enum<enum_type> operator^(enum_type lhs, bitflag_enum<enum_type> rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs)^rhs.bits()); 
} 

// The only missing pieces above are for the UxU cases 
// we allow you to have those by defining a specialization of is_bitflag_enum<>, as follows: 
// 
// template <> struct is_bitflag_enum<YourEnumType> : std::true_type { }; 
// 
// However, by default, no other types will convert to an bitflag_enum<> unless you explicitly say you want it 
// 
// If you have asked for them, then you can use MyEnum::ValueX | MyEnum::ValueY and that will produce a bitflag_enum<MyEnum> 
// so your code can simply use your enumeration values with scope and as-if they were bit flags as you would think you could 

// don't mess up existing enumerations or types by defining these global operators on every existing type! 
template <typename enum_type> struct is_bitflag_enum : std::false_type { }; 

template <typename enum_type> 
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type 
operator & (enum_type lhs, enum_type rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) & static_cast<store_type>(rhs)); 
} 

template <typename enum_type> 
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type 
operator | (enum_type lhs, enum_type rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs) | static_cast<store_type>(rhs)); 
} 

template <typename enum_type> 
typename std::enable_if<is_bitflag_enum<enum_type>::value, bitflag_enum<enum_type>>::type 
operator^(enum_type lhs, enum_type rhs) 
{ 
    using store_type = std::underlying_type<enum_type>::type; 
    return bitflag_enum<enum_type>(static_cast<store_type>(lhs)^static_cast<store_type>(rhs)); 
} 
相關問題