问题
I have code that resembles below.
typedef uint32_t IntType;
typedef IntType IntValue;
typedef boost::variant<IntValue, std::string> MsgValue;
MsgValue v;
Instead of saying this,
IntValue value = boost::apply_visitor(d_string_int_visitor(), v);
I would like to pass an extra parameter like this: But operator() gives a compile error.
//This gives an error since the overload below doesn't work.
IntValue value = boost::apply_visitor(d_string_int_visitor(), v, anotherStr);
class d_string_int_visitor : public boost::static_visitor<IntType>
{
public:
inline IntType operator()(IntType i) const
{
return i;
}
inline IntValue operator()(const std::string& str) const noexcept
{
// code in here
}
//I want this, but compiler error.
inline IntValue operator()(const std::string& str, const std::string s) const noexcept
{
// code in here
}
};
回答1:
You can bind the extra string
argument to the visitor using std::bind. First, add the std::string
parameter to all of the visitor's operator()
overloads.
class d_string_int_visitor : public boost::static_visitor<IntType>
{
public:
inline IntType operator()(IntType i, const std::string& s) const
{
return i;
}
inline IntValue operator()(const std::string& str, const std::string& s) const noexcept
{
// code in here
return 0;
}
};
Now create a visitor to which you have bound the second string
argument.
auto bound_visitor = std::bind(d_string_int_visitor(), std::placeholders::_1, "Hello World!");
boost::apply_visitor(bound_visitor, v);
Live demo
However, a better solution would be to pass the string as the visitor's constructor argument.
回答2:
typedef uint32_t IntType;
typedef IntType IntValue;
typedef boost::variant<IntValue, std::string> MsgValue;
MsgValue v;
IntValue value = boost::apply_visitor([&](auto&& one){
return d_string_int_visitor{}(decltype(one)(one), anotherStr);
}, v);
assuming every overload of d_string_int_visitor
can handle the extra parameter.
As a bonus, you can even do away with the wrapping class if you want:
IntValue to_int_value(IntValue v, std::string const& format) { return v; }
IntValue to_int_value(std::string const& str, std::string const& format);
IntValue value = boost::apply_visitor([&](auto&& one){
return to_int_value(decltype(one)(one), anotherStr);
}, v);
where we create an anonymous lambda that forwards to a traditional set of function overloads.
The auto&& one
and decltype(one)(one)
is a technique to do perfect forwarding from a lambda (C++14). You could replace the second with std::forward<decltype(one)>(one)
, but I find the short version readable. Unlike std::forward
, it does the "wrong" thing with value-types, but we know that one
is an l or r value reference.
来源:https://stackoverflow.com/questions/29618636/boost-variant-visitor-with-an-extra-parameter