Классификация типов в варианте Boost с использованием посетителя не компилируется

Я использую boost-variant во всех своих проектах. В одном сценарии мне нужно классифицировать типы, содержащиеся в моем boost-variant, на несколько классов. Так как у меня довольно много типов в моем варианте, я пришел к идее определить несколько вариантов внутри моего посетителя. Эти варианты в основном представляют собой сопоставление типа-> класса.

Код ниже иллюстрирует то, чего я хотел достичь.

    #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;
    }
}

person Aleph0    schedule 02.03.2017    source источник


Ответы (1)


#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;
    }
};

ДЕМО

person Piotr Skotnicki    schedule 02.03.2017
comment
Прохладный. Я также получил это решение сейчас. Кажется, это единственный рабочий подход. Но чтение может вызвать у вас головную боль. :-) Я также опубликую свое решение. Большое спасибо. - person Aleph0; 02.03.2017
comment
Дополнительные альтернативы @FrankSimon включают: отправку тегов и добавление отдельной перегрузки для каждого типа. - person Piotr Skotnicki; 02.03.2017
comment
Понятия не имею, что такое диспетчеризация тегов. Я думаю, что я должен узнать немного больше. :-) - person Aleph0; 02.03.2017