boost::apply_visitor(const& visitor, ...) - why const?
Hello, Why does boost::apply_visitor take a const reference to the visitor object? This forces me to write a boost::static_visitor-derived visitor that looks like this: class my_visitor_t : public boost::static_visitor<> { template <typename T> void operator()(T const& t) const { // do something ... } }; ... when what I would really like to do is something like this: class my_visitor_t : public boost::static_visitor<> { my_visitor(my_data & _data) : data(_data) {} void operator()(int const& i) // NOT const { // data.blah.blah = // blah } template <typename T> void operator()(T const& t) // NOT const { // data.foo() } my_data & data }; So I might have several different instances of a given a my_variant_t: my_variant_t v1; my_variant_t v2; my_variant_t v3; my_data_t my_data; my_visitor_t my_visitor(my_data); boost::apply_visitor(my_visitor, v1); boost::apply_visitor(my_visitor, v2); // and so on... ... BUT, the fact that I must make the visitor operator() const makes this impossible and is inconsistent with the way that I'm used to leveraging visitors in the BGL. Would someone be kind enough to comment on the rationale behind this? What am I missing here? - Thanks Chris
On 1/18/07, cdr@encapsule.com
Hello,
Why does boost::apply_visitor take a const reference to the visitor object? This forces me to write a boost::static_visitor-derived visitor that looks like this:
class my_visitor_t : public boost::static_visitor<> { template <typename T> void operator()(T const& t) const { // do something ... }
};
... when what I would really like to do is something like this:
class my_visitor_t : public boost::static_visitor<> { my_visitor(my_data & _data) : data(_data) {}
void operator()(int const& i) // NOT const { // data.blah.blah = // blah }
template <typename T> void operator()(T const& t) // NOT const { // data.foo() }
my_data & data
};
So I might have several different instances of a given a my_variant_t:
my_variant_t v1; my_variant_t v2; my_variant_t v3;
my_data_t my_data;
my_visitor_t my_visitor(my_data);
boost::apply_visitor(my_visitor, v1); boost::apply_visitor(my_visitor, v2); // and so on...
... BUT, the fact that I must make the visitor operator() const makes this impossible and is inconsistent with the way that I'm used to leveraging visitors in the BGL. Would someone be kind enough to comment on the rationale behind this? What am I missing here?
Assuming you dont want to return the results of the execution of the visitor into my_data, but to really use it in a non-const way, why dont you do something like this: struct my_visitor { typedef void result_type; void operator()(int const&, data&) const { } template <typename T> void operator()(T const&, data&) const { } }; my_visitor v; boost::apply_visitor(boost::bind(v, my_data), v1); Now, if you want to return from the visitor, you should use: struct visitor { typedef data result_type; data operator()(int const&) const; template <typename T> data operator()(T const&) const; }; data d = boost::apply_visitor(visitor(), v1); It have worked for me for a time.
- Thanks
Chris
Best regards, -- Felipe Magno de Almeida
Thank you for this work-around Felipe - that will get the job done. Still -
it seems rather a convoluted way to accomplish a task that could be done
with little fuss if boost::apply_visitor accepted a non-const reference to
the visitor instance. I'll use your method to get my task complete but would
still like to understand why it was done this way.
- Thanks
"Felipe Magno de Almeida"
On 1/18/07, cdr@encapsule.com
wrote: Hello,
Why does boost::apply_visitor take a const reference to the visitor object? This forces me to write a boost::static_visitor-derived visitor that looks like this:
class my_visitor_t : public boost::static_visitor<> { template <typename T> void operator()(T const& t) const { // do something ... }
};
... when what I would really like to do is something like this:
class my_visitor_t : public boost::static_visitor<> { my_visitor(my_data & _data) : data(_data) {}
void operator()(int const& i) // NOT const { // data.blah.blah = // blah }
template <typename T> void operator()(T const& t) // NOT const { // data.foo() }
my_data & data
};
So I might have several different instances of a given a my_variant_t:
my_variant_t v1; my_variant_t v2; my_variant_t v3;
my_data_t my_data;
my_visitor_t my_visitor(my_data);
boost::apply_visitor(my_visitor, v1); boost::apply_visitor(my_visitor, v2); // and so on...
... BUT, the fact that I must make the visitor operator() const makes this impossible and is inconsistent with the way that I'm used to leveraging visitors in the BGL. Would someone be kind enough to comment on the rationale behind this? What am I missing here?
Assuming you dont want to return the results of the execution of the visitor into my_data, but to really use it in a non-const way, why dont you do something like this:
struct my_visitor { typedef void result_type;
void operator()(int const&, data&) const { }
template <typename T> void operator()(T const&, data&) const { } };
my_visitor v;
boost::apply_visitor(boost::bind(v, my_data), v1);
Now, if you want to return from the visitor, you should use:
struct visitor { typedef data result_type;
data operator()(int const&) const; template <typename T> data operator()(T const&) const; };
data d = boost::apply_visitor(visitor(), v1);
It have worked for me for a time.
- Thanks
Chris
Best regards, -- Felipe Magno de Almeida
cdr@encapsule.com wrote:
Hello,
Why does boost::apply_visitor take a const reference to the visitor object? This forces me to write a boost::static_visitor-derived visitor that looks like this:
class my_visitor_t : public boost::static_visitor<> { template <typename T> void operator()(T const& t) const { // do something ... }
};
... when what I would really like to do is something like this:
class my_visitor_t : public boost::static_visitor<> { my_visitor(my_data & _data) : data(_data) {}
void operator()(int const& i) // NOT const { // data.blah.blah = // blah }
template <typename T> void operator()(T const& t) // NOT const { // data.foo() }
my_data & data
};
So I might have several different instances of a given a my_variant_t:
my_variant_t v1; my_variant_t v2; my_variant_t v3;
my_data_t my_data;
my_visitor_t my_visitor(my_data);
boost::apply_visitor(my_visitor, v1); boost::apply_visitor(my_visitor, v2); // and so on...
... BUT, the fact that I must make the visitor operator() const makes this impossible and is inconsistent with the way that I'm used to leveraging visitors in the BGL. Would someone be kind enough to comment on the rationale behind this? What am I missing here?
- Thanks
Chris
Accepting the visitor by non-const reference will make calling apply_visitor with a temporary visitor not compile, so there's a downside to that too. You can use the apply_visitor member function of variant: v1.apply_visitor(my_visitor); v2.apply_visitor(my_visitor); which accepts the visitor by non-const reference. Not that I'm saying that any if these solutions is best. IMO, a static_visitor is some kind of a functor, and as such should be accepted by value, making it possible to pass temporaries, and write code as you described. However, I couldn't convince about it... Yuval
Thanks Yuval - this allows me to do exactly what I want.
It would be useful to add this information to the high-level class overview
http://boost.org/doc/html/variant/reference.html#header.boost.variant.hpp
and to the Basic Usage tutorial section
http://boost.org/doc/html/variant/tutorial.html#variant.tutorial.basic which
doesn't mention this useful detail.
To your point about passing the visitor by value: I don't understand why
this would be useful. One could for example simply call like:
typedef boost::variant
Accepting the visitor by non-const reference will make calling apply_visitor with a temporary visitor not compile, so there's a downside to that too. You can use the apply_visitor member function of variant:
v1.apply_visitor(my_visitor); v2.apply_visitor(my_visitor);
which accepts the visitor by non-const reference.
Not that I'm saying that any if these solutions is best. IMO, a static_visitor is some kind of a functor, and as such should be accepted by value, making it possible to pass temporaries, and write code as you described. However, I couldn't convince about it...
Yuval
Chris Russell wrote:
To your point about passing the visitor by value: I don't understand why this would be useful. One could for example simply call like:
typedef boost::variant
my_variant; class my_visitor : public boost::static_visitor<> { //... }; my_variant v(5); v.apply_visitor(my_visitor());
This won't compile - as I said, you can't pass a temporary as an argument to a function that takes its argument by non-const reference (in a conforming compiler, that is).
... which constructs the visitor as a temporary but doesn't incur the overhead of an extra copy constructor.
The copy constructor should be a non-issue, just as is the case with functors. Why don't you complain that std::for_each accept its functor by value? Because copy construct of a functor should be trivial - copies references at most. static_visitors are just like functors, IMO.
Anyway - thanks very much for pointing out that I can call apply_visitor directly on the variant. This saves me time and makes my code more readable than the previous suggestion of using boost::bind to adapt the unary visitor functors into binary functors that accept a non-const reference to the data I want to operate on in my visitor's operator().
You're welcome :)
participants (4)
-
cdr@encapsule.com
-
Chris Russell
-
Felipe Magno de Almeida
-
Yuval Ronen