我经常在我的项目中使用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
。
我们做错了什么?
答案 0 :(得分:4)
apply_visitor
的重载仅限访问者。这会返回一个符合您需要的部分应用的函数对象,我想:
此重载返回
就个人而言,我喜欢在访问者对象中添加一个重载:
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_visitor
与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;
}
答案 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;
}