Discussion:
boost::apply_visitor(const& visitor, ...) - why const?
c***@encapsule.com
2007-01-19 00:54:29 UTC
Permalink
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
Felipe Magno de Almeida
2007-01-19 01:19:27 UTC
Permalink
Post by c***@encapsule.com
Hello,
Why does boost::apply_visitor take a const reference to the visitor object? This forces me to write a
class my_visitor_t : public boost::static_visitor<>
{
template <typename T> void operator()(T const& t) const
{ // do something ... }
};
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
};
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.
Post by c***@encapsule.com
- Thanks
Chris
Best regards,
--
Felipe Magno de Almeida
Chris Russell
2007-01-19 01:37:31 UTC
Permalink
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
Post by Felipe Magno de Almeida
Post by c***@encapsule.com
Hello,
Why does boost::apply_visitor take a const reference to the visitor
object? This forces me to write a
class my_visitor_t : public boost::static_visitor<>
{
template <typename T> void operator()(T const& t) const
{ // do something ... }
};
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
};
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
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);
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.
Post by c***@encapsule.com
- Thanks
Chris
Best regards,
--
Felipe Magno de Almeida
Yuval Ronen
2007-01-19 10:40:39 UTC
Permalink
Post by c***@encapsule.com
Hello,
Why does boost::apply_visitor take a const reference to the visitor object? This forces me to write a
class my_visitor_t : public boost::static_visitor<>
{
template <typename T> void operator()(T const& t) const
{ // do something ... }
};
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
};
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
Chris Russell
2007-01-19 16:04:55 UTC
Permalink
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<int,long,string> my_variant;
class my_visitor : public boost::static_visitor<>
{ //... };
my_variant v(5);
v.apply_visitor(my_visitor());

... which constructs the visitor as a temporary but doesn't incur the
overhead of an extra copy constructor.

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().

- Regards

Chris
Post by Yuval Ronen
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
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
Yuval Ronen
2007-01-19 17:27:44 UTC
Permalink
Post by Chris Russell
To your point about passing the visitor by value: I don't understand why
typedef boost::variant<int,long,string> 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).
Post by Chris Russell
... 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.
Post by Chris Russell
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 :)

Loading...