2017-03-02 33 views
3

我在整個項目中都使用boost-variant。在一種情況下,我需要將包含在我的boost-variant中的類型分爲幾類。由於我的變體中有很多類型,我想到了在訪問者中定義幾個變體的想法。這些變體基本上是type-> class映射。使用訪問者的Boost變體中的分類類型無法編譯

下面的代碼說明了我想達到的目的。

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

    enum class Type { 
     Integer, 
     Float, 
     NonNumeric 
    }; 

    struct IsNum : public boost::static_visitor<Type> { 
     typedef boost::variant<int, size_t> IntegerTypes; 
     typedef boost::variant<float, double> FloatTypes; 
     typedef boost::variant<std::string> NonNumericTypes; 

     result_type operator()(const IntegerTypes& t) const { 
      return Type::Integer; 
     } 

     result_type operator()(const FloatTypes& t) const { 
      return Type::Float; 
     } 

     result_type operator()(const NonNumericTypes& t) const { 
      return Type::NonNumeric; 
     } 
    }; 

    int main() { 
     boost::variant<int, std::string, double> value; 
     value = 5; 
     IsNum visitor; 
     auto result = value.apply_visitor(visitor); 
    } 

不幸的是,代碼不會編譯。 MSVC以編譯器錯誤C3066結束。用這個參數來調用這個類型的對象有不同的可能性嗎?它可能是三個operator()函數之一。

但基本上,我可以將5只轉換爲變體類型IntegerTypes

什麼可能是這種問題的解決方案?

自己的解決方案

嘗試使用boost-mpl我來到這個解決方案之後。函數Contains代表一個可重用的軟件,可能包含在我的程序的其他部分中。不過,我希望解決方案將更接近我原來的發佈源代碼。

#include <iostream> 
#include <boost/variant.hpp> 
#include <string> 
#include <boost/mpl/contains.hpp> 

enum class Type { 
    Integer, 
    Float, 
    NonNumeric 
}; 

template<typename V, typename T> 
using ContainsImpl = typename boost::mpl::contains<typename V::types, T>::type; 

template<typename V, typename T> 
bool Contains(const T&) { 
    return ContainsImpl<V, T>::value; 
} 

struct IsNum : public boost::static_visitor<Type> { 
    typedef boost::variant<int, size_t> IntegerTypes; 
    typedef boost::variant<float, double> FloatTypes; 
    typedef boost::variant<std::string> NonNumericTypes; 

    template<typename T> 
    result_type operator()(const T& t) const { 
     if (Contains<IntegerTypes>(t)) { 
      return Type::Integer; 
     } else if (Contains<FloatTypes>(t)) { 
      return Type::Float; 
     } else if (Contains<NonNumericTypes>(t)) { 
      return Type::NonNumeric; 
     } 

     return Type::NonNumeric; 
    } 
}; 

int main() { 
    boost::variant<int, std::string, double> value; 
    value = 5.; 
    IsNum visitor; 
    auto result = value.apply_visitor(visitor); 
    if (result == Type::Integer) { 
     std::cout << "Integer" << std::endl; 
    } 
    if (result == Type::Float) { 
     std::cout << "Float" << std::endl; 
    } 
    if (result == Type::NonNumeric) { 
     std::cout << "Non Numeric" << std::endl; 
    } 
} 

回答

2
#include <string> 
#include <boost/variant.hpp> 
#include <boost/mpl/list.hpp> 
#include <boost/mpl/contains.hpp> 
#include <boost/type_traits.hpp> 

enum class Type 
{ 
    Integer, 
    Float, 
    NonNumeric 
}; 

struct IsNum : public boost::static_visitor<Type> 
{ 
    typedef boost::mpl::list<int, size_t> IntegerTypes; 
    typedef boost::mpl::list<float, double> FloatTypes; 
    typedef boost::mpl::list<std::string> NonNumericTypes; 

    template <typename T> 
    typename boost::enable_if<boost::mpl::contains<IntegerTypes, T>, result_type>::type 
    operator()(const T& t) const 
    { 
     return Type::Integer; 
    } 

    template <typename T> 
    typename boost::enable_if<boost::mpl::contains<FloatTypes, T>, result_type>::type 
    operator()(const T& t) const 
    { 
     return Type::Float; 
    } 

    template <typename T> 
    typename boost::enable_if<boost::mpl::contains<NonNumericTypes, T>, result_type>::type 
    operator()(const T& t) const 
    { 
     return Type::NonNumeric; 
    } 
}; 

DEMO

+0

酷。我現在也衍生出這個解決方案。這似乎是唯一可行的方法。但讀它可能會讓你頭痛。 :-)我也會發布我的解決方案。非常感謝。 – Aleph0

+1

@FrankSimon更多的選擇包括:標籤調度和爲每種類型添加單獨的過載 –

+0

不知道標籤調度是什麼。我會認爲我必須學習更多。 :-) – Aleph0