如何将`boost :: static_visitor`实例传递给函数

时间:2017-04-28 09:20:50

标签: c++ templates boost visitor boost-variant

我经常在我的项目中使用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 个答案:

答案 0 :(得分:4)

apply_visitor的重载仅限访问者。这会返回一个符合您需要的部分应用的函数对象,我想:

此重载返回

  

Class template apply_visitor_delayed_t boost::apply_visitor_delayed_t — Adapts a visitor for use as a function object.

就个人而言,我喜欢在访问者对象中添加一个重载:

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

这样,你可以简单地使用visitor对象作为函数对象。

答案 1 :(得分:3)

static_visitor不是多态基类,不能这样对待。它的名字就是:它是一个静态(静态类型)访问者。您需要访问者的静态类型是您要调用的类型。请注意,这是不可避免的,因为成员函数模板不能是虚拟的。在您的情况下,这意味着adder必须成为一个函数模板,由访问者类型模板化:

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

main中的使用将保持不变。

如果您无法承担adder个模板的费用,可以将@sehe's answer提到的apply_visitorstd::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;
}

[Live example]

从技术上讲,访问者本身已经是一个合适的可调用对象,所以它甚至可以在没有明确使用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;
}

答案 2 :(得分:1)

首先,您应将访问者的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;
}

live wandbox example