Boost logo

Boost Users :

From: Eric Niebler (eric_at_[hidden])
Date: 2007-07-23 18:54:24


Raider wrote:
>>
>> The const-ness of the proxy itself shouldn't matter. Only the const-ness
>> of the proxied object should matter.
>>
>
> I want const_iterator to be dereferencable to const object and iterator
> to non-cont object. Like this:
>
> class vec
> {
> public:
> vec(sv& v = global) : v_(v) {}
> typedef sv::iterator iterator;
> typedef sv::const_iterator const_iterator;
> iterator begin() const { return v_.begin(); }
> iterator end() const { return v_.end(); }
> void push_back(const string& s) const { v_.push_back(s); }
> private:
> sv& v_;
> };

It's not wrong to want that. But it is for important safety reasons that
BOOST_FOREACH treats copies of containers as const. In some of the
examples you've shown so far, you're trying to proxy a temporary object
so you can modify it. That's never going to work. Like this:

> for (xxx::iterator it(GetVector().begin(), end(GetVector().end());
> it != end; ++it) { modify(*it); }

That's not even valid C++. But even if it were and GetVector() returns a
temporary object or a proxy to a temporary object, the vector will be
destroyed before you can iterate it. The interface I chose preserves
object lifetimes and prevents inadvertent mutation of temporary objects.
Weakening its guarantees is a bad idea.

If you want the proxy to offer either a const or mutable interface *and*
you want your proxy to work with BOOST_FOREACH, you should base it on
the const-ness or mutability of the object being proxied, not the
constness of the proxy itself. Consider:

template<class Range>
struct proxy {
   typedef typename range_result_iterator<Range>::type iterator;
   typedef typename range_result_iterator<Range>::type const_iterator;
   iterator begin() const { return boost::begin(rng_); }
   iterator end() const { return boost::end(rng_); }
   // etc ...
   Range &rng_;
};

Now, you can have const and mutable proxied objects like:

   // ok, a mutable proxy
   proxy< std::vector<int> > p1;

   // ok, still a mutable proxy
   proxy< std::vector<int> > const p2;

   // ok, a const proxy
   proxy< std::vector<int> const > p3;

   // still a const proxy
   proxy< std::vector<int> const > const p4;

You can easily write a helper function to create the proxy object for
you that has the correct const behavior.

> This allows me make no difference does GetVector() return `vec' type
> (proxy) or `sv&' (reference to the real container):

As does the above.

> Now I found some unsteady way:
> #define BOOST_FOREACH_NO_RVALUE_DETECTION
> makes my code compile. I'll try to dig in this direction.

Don't do it. Bad things can happen if you use rvalues after you've
turned off foreach's rvalue detection.

-- 
Eric Niebler
Boost Consulting
www.boost-consulting.com
The Astoria Seminar ==> http://www.astoriaseminar.com

Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net