[iterator] [iterator_facade] operator-> question

I'm looking at boost::iterator_facade's implementation of operator->, and I'm not convinced it is correct when using proxy references. I ran into this problem while using iterator_facade as (sketch only) struct derived_iterator : boost::iterator_facade< derived_iterator, pair< Index, Value >, // value_type CategoryOrTraversal, pair< Index, Value& >, // reference Difference > { /*...*/ }; where pair here is similar to std::pair, but also handles reference types. Thus, the reference type of derived_iterator is a proxy reference. Indeed, this might be the basic structure of an iterator to an associative container, particularly when the keys can be generated "on the fly". The problem (at least as I see it) is in lines 295 - 329 in boost/iterator/iterator_facade.hpp (as of Boost version 1.46.1), copied below for convenience: ---------------- // operator->() needs special support for input iterators to strictly meet the // standard's requirements. If *i is not a reference type, we must still // produce a lvalue to which a pointer can be formed. We do that by // returning an instantiation of this special proxy class template. template <class T> struct operator_arrow_proxy { operator_arrow_proxy(T const* px) : m_value(*px) {} T* operator->() const { return &m_value; } // This function is needed for MWCW and BCC, which won't call operator-> // again automatically per 13.3.1.2 para 8 operator T*() const { return &m_value; } mutable T m_value; }; // A metafunction that gets the result type for operator->. Also // has a static function make() which builds the result from a // Reference template <class ValueType, class Reference, class Pointer> struct operator_arrow_result { // CWPro8.3 won't accept "operator_arrow_result::type", and we // need that type below, so metafunction forwarding would be a // losing proposition here. typedef typename mpl::if_< is_reference<Reference> , Pointer , operator_arrow_proxy<ValueType> >::type type; static type make(Reference x) { return boost::implicit_cast<type>(&x); } }; ---------------- Now, iterator_facade< /*...*/ >::operator->'s return type is operator_arrow_result< /*...*/ >::type, which, for proxy references, according to the logic above in operator_arrow_result, is operator_arrow_proxy<T>, where T, in this case, is pair< Index, Value >. The error occurs when operator_arrow_proxy<T> is constructed from &x within operator_arrow_result< /*...*/ >::make, as it tries to convert a pair< Index, Value& >* to a pair< Index, Value >*, which is a no-go. It seems to me that a more correct implementation of operator_arrow_result would use operator_arrow_proxy< Reference > rather than operator_arrow_proxy< ValueType >. I would further rename and change operator_arrow_proxy's member variable to be stored as const T m_ref, rather than mutable T m_value. What are others' opinions on this? Am I doing something wrong? Thanks, - Jeff

At Wed, 29 Jun 2011 13:09:04 -0700, Jeffrey Lee Hellrung, Jr. wrote:
That won't get us what we need: an actual T lvalue as noted in the comment.
Is that related to this problem?
What are others' opinions on this? Am I doing something wrong?
I doubt you're doing anything wrong. I'm fully prepared to believe the library should be doing something smarter. What that smarter thing is, I am not sure of, but you might find the answer in one of the many outstanding tickets. -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Thu, Jun 30, 2011 at 3:50 PM, Dave Abrahams <dave@boostpro.com> wrote:
Well to be perfectly accurate, the comment says nothing about a *T* lvalue, it only states: "If *i is not a reference type, we must still produce a lvalue to which a pointer can be formed." I'm assuming here that you meant T to be the iterator's value_type. As long as operator_arrow_proxy is wrapping an *object* (a proxy reference qualifies), you'll ultimately get a pointer to *some* lvalue through the chain of operator->'s (...right? At least I thought that's how operator-> worked...). And if you want (*i).member to be equivalent to i->member, then it seems to me that the operator_arrow_proxy has got to be wrapping the proxy reference, not a value_type object. As far as I'm concerned, the last row of the table at http://www.boost.org/libs/iterator/doc/ReadableIterator.html *almost* requires operator_arrow_proxy to store the proxy reference, not the value_type object...I say "almost" since the Concept is premised on U being a member of T, but it seems the *intent* nonetheless is for i->member to be equivalent to (*i).member. Even if you want operator_arrow_proxy to wrap a value_type object and return a value_type* for its operator->, the code is still trying to implicitly cast a reference* (or whatever the result of &(*i) is) to a value_type const *, and I just don't see how one expects that to compile generally. I.e., I don't recall any iterator requirements on the result of &(*i) being convertible to value_type const *...
No. I'm only claiming that all operations on the proxy reference should logically not mutate the proxy reference itself, only its referent, hence it should be correct to store it by constant value and return a pointer-to-const in operator->.
I will browse, and maybe add a ticket. I guess I should also check the tests to see if this case is covered. - Jeff
participants (2)
-
Dave Abrahams
-
Jeffrey Lee Hellrung, Jr.