Boost logo

Boost :

From: Giovanni Piero Deretta (gpderetta_at_[hidden])
Date: 2007-07-24 19:32:28


On 7/24/07, Eric Niebler <eric_at_[hidden]> wrote:
> General design question here. This came up on the user's list, and it
> made me question something I believed to be true. When writing a proxy
> for, e.g., a std container, I see two options for handling const.
>
> 0) Const-ness depends on the const-ness of the object being proxied:
>
> struct vector_proxy {
> std::vector<int> & vec;
> typedef std::vector<int>::iterator iterator;
> typedef std::vector<int>::iterator const_iterator;
> iterator begin() const { return vec.begin(); }
> ...
> };
>
> 1) Const-ness depends on the proxy object itself:
>
> struct vector_proxy {
> std::vector<int> & vec;
> typedef std::vector<int>::iterator iterator;
> typedef std::vector<int>::const_iterator const_iterator;
> iterator begin() { return vec.begin(); }
> const_iterator begin() const { return vec.begin(); }
> ...
> };
>
> I think a loooong time ago, I preferred (1) but these days (0) is more
> natural for me, but I can't say why, exactly. Just feels like that's how
> it should be. Thoughts?
>
> FWIW, it came up in the context of BOOST_FOREACH's handling of proxies,
> so it's not (far) off-topic.
>

Consider this:

  std::vector<int> x = ....
  for_each(filter(x, some_filter_op), do_something);

'Filter' will obviously return a proxy for 'x' (actually wrapping x
iterators with boost::filter iterators) as a temporary object.
for_each will bind the temporary to a const reference (at least until
we get rvalue references).

This means that with option 1, do_something will get const references
to int, thus cannot mutate the integers in x.

With option 0, do_something will get non const references and can
actually mutate x, and this is, I think, what would normally be
expected. I write lots of code like this and deep constness semantics
would really make it impractical. (side effect free code is nice, but
sometimes you just need them).

Even if you consider simple proxies that do not add any semantics. I
would expect
this code to do the right thing:

  std::vector<int> x;
  for_each(make_range(x.begin(), x.end()), _1 = 0)

(btw, notice that if you substitute make_range with
boost::iterator_range this works as expected, while boost::sub_range
has deep const semantics.)

just my 0.02 euros

gpd


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk