forked from boostorg/iterator
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:
@ -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
|
||||||
|
Reference in New Issue
Block a user