Added conversion from postfix increment proxy to iterator type.

This allows expressions such as `Iterator it2(it1++)` to compile.

Additionally separated operations that are allowed on the result of
dereferencing the proxy to a separate set of proxies to make the allowed
set of expressions more strict and unambiguous. In particular, it means
`it++` cannot be converted to value type and `*it++` cannot be converted
to the iterator type anymore. Also, make sure `*it1++ = *it2++` works
as expexted by explicitly converting the proxy to the value type on
assignment.

Fixes https://github.com/boostorg/iterator/issues/75.
This commit is contained in:
Andrey Semashev
2022-11-09 21:06:41 +03:00
parent 853ba3d3c7
commit 5777e9944b

View File

@ -150,72 +150,54 @@ namespace iterators {
// value must be read and stored away before the increment occurs // value must be read and stored away before the increment occurs
// so that *a++ yields the originally referenced element and not // so that *a++ yields the originally referenced element and not
// the next one. // the next one.
template <class Value>
class postfix_increment_dereference_proxy
{
typedef Value value_type;
public:
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template<typename OtherValue>
explicit postfix_increment_dereference_proxy(OtherValue&& x)
: stored_value(static_cast< OtherValue&& >(x))
{}
#else
explicit postfix_increment_dereference_proxy(value_type const& x)
: stored_value(x)
{}
explicit postfix_increment_dereference_proxy(value_type& x)
: stored_value(x)
{}
#endif
// Returning a mutable reference allows nonsense like
// (*r++).mutate(), but it imposes fewer assumptions about the
// behavior of the value_type. In particular, recall that
// (*r).mutate() is legal if operator* returns by value.
// Provides readability of *r++
operator value_type&() const
{
return this->stored_value;
}
private:
mutable value_type stored_value;
};
template <class Iterator> template <class Iterator>
class postfix_increment_proxy class postfix_increment_proxy
{ {
typedef typename iterator_value<Iterator>::type value_type; typedef typename iterator_value<Iterator>::type value_type;
public: public:
explicit postfix_increment_proxy(Iterator const& x) explicit postfix_increment_proxy(Iterator const& x)
: stored_value(*x) : stored_iterator(x)
, dereference_proxy(*x)
{} {}
// Returning a mutable reference allows nonsense like postfix_increment_dereference_proxy<value_type> const&
// (*r++).mutate(), but it imposes fewer assumptions about the
// behavior of the value_type. In particular, recall that
// (*r).mutate() is legal if operator* returns by value.
value_type&
operator*() const operator*() const
{ {
return this->stored_value; return dereference_proxy;
}
private:
mutable value_type stored_value;
};
//
// In general, we can't determine that such an iterator isn't
// writable -- we also need to store a copy of the old iterator so
// that it can be written into.
template <class Iterator>
class writable_postfix_increment_proxy
{
typedef typename iterator_value<Iterator>::type value_type;
public:
explicit writable_postfix_increment_proxy(Iterator const& x)
: stored_value(*x)
, stored_iterator(x)
{}
// Dereferencing must return a proxy so that both *r++ = o and
// value_type(*r++) can work. In this case, *r is the same as
// *r++, and the conversion operator below is used to ensure
// readability.
writable_postfix_increment_proxy const&
operator*() const
{
return *this;
}
// Provides readability of *r++
operator value_type&() const
{
return stored_value;
}
// Provides writability of *r++
template <class T>
T const& operator=(T const& x) const
{
*this->stored_iterator = x;
return x;
}
// This overload just in case only non-const objects are writable
template <class T>
T& operator=(T& x) const
{
*this->stored_iterator = x;
return x;
} }
// Provides X(r++) // Provides X(r++)
@ -225,8 +207,122 @@ namespace iterators {
} }
private: private:
mutable value_type stored_value;
Iterator stored_iterator; Iterator stored_iterator;
postfix_increment_dereference_proxy<value_type> dereference_proxy;
};
template <class Iterator>
class writable_postfix_increment_dereference_proxy;
template <class T>
struct is_not_writable_postfix_increment_dereference_proxy :
public boost::true_type
{};
template <class Iterator>
struct is_not_writable_postfix_increment_dereference_proxy<
writable_postfix_increment_dereference_proxy<Iterator>
> :
public boost::false_type
{};
template <class Iterator>
class writable_postfix_increment_proxy;
//
// In general, we can't determine that such an iterator isn't
// writable -- we also need to store a copy of the old iterator so
// that it can be written into.
template <class Iterator>
class writable_postfix_increment_dereference_proxy
{
friend class writable_postfix_increment_proxy<Iterator>;
typedef typename iterator_value<Iterator>::type value_type;
public:
explicit writable_postfix_increment_dereference_proxy(Iterator const& x)
: stored_iterator(x)
, stored_value(*x)
{}
// Provides readability of *r++
operator value_type&() const
{
return this->stored_value;
}
template <class OtherIterator>
writable_postfix_increment_dereference_proxy const&
operator=(writable_postfix_increment_dereference_proxy<OtherIterator> const& x) const
{
typedef typename iterator_value<OtherIterator>::type other_value_type;
*this->stored_iterator = static_cast<other_value_type&>(x);
return *this;
}
// Provides writability of *r++
#if !defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <class T>
typename iterators::enable_if<
is_not_writable_postfix_increment_dereference_proxy< T >,
writable_postfix_increment_dereference_proxy const&
>::type operator=(T&& x) const
{
*this->stored_iterator = static_cast< T&& >(x);
return *this;
}
#else
template <class T>
typename iterators::enable_if<
is_not_writable_postfix_increment_dereference_proxy< T >,
writable_postfix_increment_dereference_proxy const&
>::type operator=(T const& x) const
{
*this->stored_iterator = x;
return *this;
}
// This overload just in case only non-const objects are writable
template <class T>
typename iterators::enable_if<
is_not_writable_postfix_increment_dereference_proxy< T >,
writable_postfix_increment_dereference_proxy const&
>::type operator=(T& x) const
{
*this->stored_iterator = x;
return *this;
}
#endif
private:
Iterator stored_iterator;
mutable value_type stored_value;
};
template <class Iterator>
class writable_postfix_increment_proxy
{
public:
explicit writable_postfix_increment_proxy(Iterator const& x)
: dereference_proxy(x)
{}
writable_postfix_increment_dereference_proxy<Iterator> const&
operator*() const
{
return dereference_proxy;
}
// Provides X(r++)
operator Iterator const&() const
{
return dereference_proxy.stored_iterator;
}
private:
writable_postfix_increment_dereference_proxy<Iterator> dereference_proxy;
}; };
# ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION # ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION