2012-02-05 65 views
3

我在代碼中使用了很多變體,我需要與某些地方的內容進行比較,以測試變體的值。如何在包含類型的變體上使用比較運算符?

例如:

if(equals<int>(aVariant, 0)){ 
    //Something 
} else { 
    //Something else 
} 

與我已經爲這個目的寫了這個簡單的模板功能:

template<typename V, typename T> 
inline bool equals(V& variant, T value){ 
    return boost::get<T>(&variant) && boost::get<T>(variant) == value; 
} 

這種運作良好,但代碼開始難以閱讀。我更喜歡使用比較操作這樣的:

if(aVariant == 0){ 
    //Something 
} else { 
    //Something else 
} 

但我不能來與運營商的有效實施。問題是==運算符已經在變體中實現,在編譯時失敗...

有人知道一種方法來實現它嗎?或者一種方法來禁用此限制?即使我必須爲變體中包含的每種可能類型實現一個版本,這也不是問題。

感謝

+2

靜態訪客? – 2012-02-05 21:35:24

+0

@ R.MartinhoFernandes我不認爲他會得到他想要的語法。 – pmr 2012-02-05 21:50:12

+0

哦,我不知道這種情況是由變體開發者明確禁止的。任何人都知道理由是什麼? – 2012-02-05 22:07:19

回答

7

至於評論,我想解決這個難題最乾淨的方法是提高boost::variant<>與運營商策略的實施(每個操作員,真的),允許客戶進行的外部應用覆蓋行爲。 (顯然這是很多通用編程工作)。

I 已實施解決方法。這使您可以實現變體的自定義運算符,即使它具有在boost/variant.hpp中實現的運算符。

我的腦電波是用BOOST_STRONG_TYPEDEF

的想法是要打破重載解析(或至少使我們的定製化重載最佳分辨率)通過使不同實際類型我們的變種(它提醒有點「絕望」 ADL障礙的:你不能從範圍非using可見的名稱,你不能去「非軍事命名空間」(障礙),因爲衝突的聲明在類的命名空間本身居住;但你可以使他們不能申請到你的'誘餌'類型)。

唉,對於operator<和家族來說效果不好,因爲boost strong-typedef實際上很難用'base'類型來保留(弱)總排序語義。在正常的英文中:strong typedefs定義operator<以及委託給基類型的實現)。

不用擔心,我們可以做一個CUSTOM_STRONG_TYPEDEF並在我們的快樂方式。查看主要的測試用例來驗證概念(下面的輸出)。

由於所描述的有趣的交互,我拿起operator<這個演示,但我想不會有你的方式來獲得一個自定義operator==去爲你的變量類型什麼。

#include <boost/variant.hpp> 
#include <boost/lexical_cast.hpp> 
#include <string> 
#include <iostream> 

///////////////////////////////////////////////////// 
// copied and reduced from boost/strong_typedef.hpp 
#define CUSTOM_STRONG_TYPEDEF(T, D)         \ 
struct D               \ 
    /*: boost::totally_ordered1< D   */      \ 
    /*, boost::totally_ordered2< D, T  */      \ 
    /*> >         */      \ 
{                 \ 
    T t;               \ 
    explicit D(const T t_) : t(t_) {};        \ 
    D(){};               \ 
    D(const D & t_) : t(t_.t){}          \ 
    D & operator=(const D & rhs) { t = rhs.t; return *this;}  \ 
    D & operator=(const T & rhs) { t = rhs; return *this;}   \ 
    operator const T &() const {return t; }      \ 
    operator T &() { return t; }         \ 
    /*bool operator==(const D & rhs) const { return t == rhs.t; } */\ 
    /*bool operator<(const D & rhs) const { return t < rhs.t; } */\ 
}; 

namespace detail 
{ 
    typedef boost::variant<unsigned int, std::string> variant_t; 

    struct less_visitor : boost::static_visitor<bool> 
    { 
     bool operator()(const std::string& a, int b) const 
     { return boost::lexical_cast<int>(a) < b; } 

     bool operator()(int a, const std::string& b) const 
     { return a < boost::lexical_cast<int>(b); } 

     template <typename T> 
      bool operator()(const T& a, const T& b) const 
      { return a < b; } 
    }; 

    struct variant_less 
    { 
     less_visitor _helper; 

     bool operator()(const variant_t& a, const variant_t& b) const 
     { return boost::apply_visitor(_helper, a, b); } 
    }; 
} 

CUSTOM_STRONG_TYPEDEF(detail::variant_t, custom_vt); 

namespace 
{ 
    bool operator<(const custom_vt& a, const custom_vt& b) 
     { return detail::variant_less()(a, b); } 

    std::ostream& operator<<(std::ostream& os, const custom_vt& v) 
     { return os << (const detail::variant_t&)v; } 
} 

int main() 
{ 
    const detail::variant_t I(43), S("42"); 
    const custom_vt i(I), s(S); 

    // regression test (compare to boost behaviour) 
    std::cout << "boost: " << I << " < " << S << ": " << std::boolalpha << (I<S) << "\n"; 
    std::cout << "boost: " << S << " < " << I << ": " << std::boolalpha << (S<I) << "\n"; 

    // FIX1: clumsy syntax (works for boost native variants) 
    detail::variant_less pred; 
    std::cout << "clumsy: " << i << " < " << s << ": " << std::boolalpha << pred(i,s) << "\n"; 
    std::cout << "clumsy: " << s << " < " << i << ": " << std::boolalpha << pred(s,i) << "\n"; 

    std::cout << "clumsy: " << I << " < " << S << ": " << std::boolalpha << pred(I,S) << "\n"; 
    std::cout << "clumsy: " << S << " < " << I << ": " << std::boolalpha << pred(S,I) << "\n"; 

    // FIX2: neat syntax (requires a custom type wrapper) 
    std::cout << "custom: " << i << " < " << s << ": " << std::boolalpha << (i<s) << "\n"; 
    std::cout << "custom: " << s << " < " << i << ": " << std::boolalpha << (s<i) << "\n"; 

} 

輸出:

boost: 43 < 42: true 
boost: 42 < 43: false 
clumsy: 43 < 42: false 
clumsy: 42 < 43: true 
clumsy: 43 < 42: false 
clumsy: 42 < 43: true 
custom: 43 < 42: false 
custom: 42 < 43: true 

現在當然也有可能,如果你想你的custom_vt傳遞到使用TMP作用於變型庫的API是不幸的相互作用。但是,由於兩者之間無痛的轉換,您應該能夠在適當的時候使用detail :: variant_t「爲自己的方式」奮鬥。

這是您爲了在呼叫站點獲得句法便利而必須付出的代價。

+0

它工作得很好:)我只需添加一些功能,以與自定義類型上的變體相同的方式工作。非常感謝。 – 2012-02-06 13:20:08