Как передать экземпляры `boost::static_visitor` в функции

Я использую boost::variant довольно часто в моих проектах. Теперь моим коллегам пришла в голову идея раздавать случаи boost::static_visitor<int> для того, чтобы настроить тип посещения. У нее был какой-то код, подобный следующему:

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

typedef boost::variant<int, std::string> TVar;

struct Visitor1 : public boost::static_visitor<int> {
    template<typename T> 
    result_type operator()(const T&) {
        return 42;
    }
};

struct Visitor2 : public boost::static_visitor<int> {
    template<typename T>
    result_type operator()(const T&) {
        return 21;
    }
};

 int adder(const boost::static_visitor<int>& visitor, const TVar& visitable) {
     return visitable.apply_visitor(visitor) + 1;
 }

int main(int argc, char **args) {
    Visitor1 v1;
    Visitor2 v2;
    TVar x;

    std::cout << adder(v1, x) << std::endl;
    std::cout << adder(v2, x) << std::endl;
}

Это выглядит идеально звучащим для меня, но не компилируется. Мой компилятор говорит, что expression will not yield a function that takes one 1 argument где-то инсайдер.

Что мы делаем не так?

3 ответа

Решение

Во-первых, вы должны отметить своих посетителей operator() перегрузки как const:

struct Visitor1 : public boost::static_visitor<int> {
    template<typename T> 
    result_type operator()(const T&) const {
        return 42;
    }
};

struct Visitor2 : public boost::static_visitor<int> {
    template<typename T>
    result_type operator()(const T&) const {
        return 21;
    }
};

Затем вам нужно изменить свой сумматор так, чтобы он принимал параметр шаблона вместо boost::static_visitor - это твое Visitor1 тип, который содержит operator() перегружать желаемой логикой. Вам нужен "правильный тип" для посещения.

template <typename T>
int adder(const T& visitor, const TVar& visitable) {
    return visitable.apply_visitor(visitor) + 1;
}

пример живой wandbox

Там перегрузка для apply_visitor это берет только посетителя. Это возвращает частично примененный объект функции, который соответствует вашим потребностям, я полагаю:

Эта перегрузка возвращает

Шаблон класса apply_visitor_delayed_t boost::apply_visitor_delayed_t - Адаптирует посетителя для использования в качестве функционального объекта.

Лично мне нравится добавлять перегрузку к объекту посетителя следующим образом:

struct MyVisitor {
    typedef void result_type;

    template <typename... Ts>
    result_type operator()(boost::variant<Ts...> const& v) const {
         return boost::apply_visitor(*this, v);
    }
    // optionally repeat for non-const `v`

    // normal variant handling overloads
};

Таким образом, вы можете просто использовать объект посетителя как функцию oject.

static_visitor не является полиморфным базовым классом и не может рассматриваться как таковой. Это в самом названии: это статический (статически типизированный) посетитель. Вам нужен статический тип посетителя, который вы хотите вызвать. Обратите внимание, что это неизбежно, поскольку шаблоны функций-членов не могут быть виртуальными. В вашем случае это означает adder должен стать шаблоном функции, с типом посетителя:

template <class Visitor>
int adder(const Visitor& visitor, const TVar& visitable) {
  return visitable.apply_visitor(visitor) + 1;
}

Использовать в main будет оставаться таким же.

Если вы не можете позволить себе сделать adder шаблон, вы можете использовать apply_visitor упомянутый ответом @sehe в сочетании с std::function (или же boost::function если вы не можете использовать C++ 11):

int adder(std::function<int(const TVar&)> visitorApplier, const TVar& visitable)
{
  return visitorApplier(visitable);
}

int main(int argc, char **args) {
    Visitor1 v1;
    Visitor2 v2;
    TVar x;

    std::cout << adder(boost::apply_visitor(v1), x) << std::endl;
    std::cout << adder(boost::apply_visitor(v2), x) << std::endl;
}

[Живой пример]

Технически, посетитель уже является подходящим вызываемым объектом, поэтому он должен работать даже без явного использования apply_visitor:

int main(int argc, char **args) {
    Visitor1 v1;
    Visitor2 v2;
    TVar x;

    std::cout << adder(v1, x) << std::endl;
    std::cout << adder(v2, x) << std::endl;
}
Другие вопросы по тегам