boost :: static_visitor не удалось специализировать шаблон функции с несколькими различными возможными типами - PullRequest
2 голосов
/ 29 апреля 2020

Я пытаюсь создать функцию посетителя, которая будет складывать значения моего boost::variant. Я использую шаблоны для случая, когда типы отличаются, например int + float

typedef boost::variant<int, float> Values;

struct Add : public boost::static_visitor<Values> {
    template <typename T, typename U>
    auto operator() (T a, U b) const -> decltype(a + b) {
        return a + b;
    }
}

Это компилируется и отлично работает

std::cout << boost::apply_visitor(Add{}, (Values)2, (Values)5) << std::endl;
std::cout << boost::apply_visitor(Add{}, (Values)2, (Values)5.123) << std::endl;

7

7.123

Однако я также хочу добавить std::string в вариант Values, чтобы я также мог сложить вместе строки. Я знаю, что, например, string + int сделать невозможно, но я постараюсь, чтобы оба из Values были строкой, прежде чем пытаться пропустить их через посетителя.

typedef boost::variant<int, float, std::string> Values;
std::cout << boost::apply_visitor(Add{}, (Values)"hello", (Values)"world") << std::endl;

Однако программа не компилируется, выдавая ошибку:

Не удалось специализировать шаблон функции 'неизвестный тип Add :: operator () (T, U) const'

Я знаю, что std::string является объектом, а не типом, и поэтому эта ошибка имеет смысл, поэтому я пытаюсь сделать особый случай, перегружая operator в структуре Add, когда входные данные являются обеими строками :

auto operator() (std::string a, std::string b) const {
    return a + b;
}

Однако я получаю ошибку

std :: basic_string, std :: allocator> Add :: operator () (std :: string, std :: string ) const ': невозможно преобразовать аргумент 1 из' T 'в' std :: string '

Похоже, он все еще пытается запустить строковый аргумент через шаблонного посетителя. Куда я иду не так? Есть ли лучший способ достичь того, что я пытаюсь сделать в целом? Извините, если ответ очевиден, я все еще довольно плохо знаком с C ++, Boost и шаблонами.

1 Ответ

2 голосов
/ 29 апреля 2020

apply_visitor должен обрабатывать все комбинации (даже если вы недействительны).

Вы можете сделать:

using Values = boost::variant<int, float, std::string>;

// Helper for overload priority
struct low_priority {};
struct high_priority : low_priority{};

struct Add : public boost::static_visitor<Values> {

    template <typename T, typename U>
    auto operator() (high_priority, T a, U b) const -> decltype(Values(a + b)) {
        return a + b;
    }

    template <typename T, typename U>
    Values operator() (low_priority, T, U) const {
        // string + int, float + string, ...
        throw std::runtime_error("Incompatible arguments");
    }

    template <typename T, typename U>
    Values operator() (T a, U b) const {
        return (*this)(high_priority{}, a, b);
    }
};
...