mirror of
https://github.com/boostorg/iterator.git
synced 2025-05-09 23:23:54 +00:00
Fixed accessing members of the dereferenced value after iterator post-increment.
The recent commit 5777e9944bf275e7e19e72e88819ec06fea670c3 broke code such as (*it++).foo(), where the result of dereferencing would be convertible to the value type but did not provide the members of the value type. To mitigate this, return a reference to the value instead of a proxy object. This will only work for non-writable iterators (and it didn't work for writable iterators before either) because in that case a proxy is needed to be able to intercept operator=. Also fix a similar issue with (it++)->foo() by adding operator-> overloads to the post-increment result proxies. Added tests for the fixes.
This commit is contained in:
parent
0a95636faf
commit
7c9b4296a1
@ -150,54 +150,25 @@ namespace iterators {
|
||||
// value must be read and stored away before the increment occurs
|
||||
// so that *a++ yields the originally referenced element and not
|
||||
// the next one.
|
||||
template <class Value>
|
||||
class postfix_increment_dereference_proxy
|
||||
template <class Iterator>
|
||||
class postfix_increment_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)
|
||||
{}
|
||||
typedef typename iterator_value<Iterator>::type value_type;
|
||||
|
||||
explicit postfix_increment_dereference_proxy(value_type& x)
|
||||
: stored_value(x)
|
||||
public:
|
||||
explicit postfix_increment_proxy(Iterator const& x)
|
||||
: stored_iterator(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
|
||||
value_type& operator*() const
|
||||
{
|
||||
return this->stored_value;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable value_type stored_value;
|
||||
};
|
||||
|
||||
template <class Iterator>
|
||||
class postfix_increment_proxy
|
||||
{
|
||||
typedef typename iterator_value<Iterator>::type value_type;
|
||||
public:
|
||||
explicit postfix_increment_proxy(Iterator const& x)
|
||||
: stored_iterator(x)
|
||||
, dereference_proxy(*x)
|
||||
{}
|
||||
|
||||
postfix_increment_dereference_proxy<value_type> const&
|
||||
operator*() const
|
||||
{
|
||||
return dereference_proxy;
|
||||
return stored_value;
|
||||
}
|
||||
|
||||
// Provides X(r++)
|
||||
@ -206,9 +177,15 @@ namespace iterators {
|
||||
return stored_iterator;
|
||||
}
|
||||
|
||||
// Provides (r++)->foo()
|
||||
value_type* operator->() const
|
||||
{
|
||||
return boost::addressof(stored_value);
|
||||
}
|
||||
|
||||
private:
|
||||
Iterator stored_iterator;
|
||||
postfix_increment_dereference_proxy<value_type> dereference_proxy;
|
||||
mutable value_type stored_value;
|
||||
};
|
||||
|
||||
|
||||
@ -304,6 +281,8 @@ namespace iterators {
|
||||
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)
|
||||
: dereference_proxy(x)
|
||||
@ -321,6 +300,12 @@ namespace iterators {
|
||||
return dereference_proxy.stored_iterator;
|
||||
}
|
||||
|
||||
// Provides (r++)->foo()
|
||||
value_type* operator->() const
|
||||
{
|
||||
return boost::addressof(dereference_proxy.stored_value);
|
||||
}
|
||||
|
||||
private:
|
||||
writable_postfix_increment_dereference_proxy<Iterator> dereference_proxy;
|
||||
};
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include <boost/call_traits.hpp>
|
||||
#include <boost/polymorphic_cast.hpp>
|
||||
#include <boost/type_traits/is_convertible.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
#include <boost/core/enable_if.hpp>
|
||||
|
||||
// This is a really, really limited test so far. All we're doing
|
||||
// right now is checking that the postfix++ proxy for single-pass
|
||||
@ -63,7 +63,23 @@ struct proxy
|
||||
|
||||
struct value
|
||||
{
|
||||
void mutator() {} // non-const member function
|
||||
int increment_count;
|
||||
int private_mutator_count;
|
||||
int& shared_mutator_count;
|
||||
|
||||
explicit value(int& shared_mutator_count) :
|
||||
increment_count(0),
|
||||
private_mutator_count(0),
|
||||
shared_mutator_count(shared_mutator_count)
|
||||
{
|
||||
}
|
||||
|
||||
// non-const member function
|
||||
void mutator()
|
||||
{
|
||||
++private_mutator_count;
|
||||
++shared_mutator_count;
|
||||
}
|
||||
};
|
||||
|
||||
struct input_iter
|
||||
@ -75,21 +91,25 @@ struct input_iter
|
||||
>
|
||||
{
|
||||
public:
|
||||
input_iter() {}
|
||||
explicit input_iter(value& val) : state(&val) {}
|
||||
|
||||
void increment()
|
||||
{
|
||||
++(state->increment_count);
|
||||
}
|
||||
value
|
||||
dereference() const
|
||||
{
|
||||
return value();
|
||||
return *state;
|
||||
}
|
||||
|
||||
bool equal(input_iter const&) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
value* state;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
@ -198,13 +218,30 @@ int main()
|
||||
{
|
||||
// test for a fix to http://tinyurl.com/zuohe
|
||||
// These two lines should be equivalent (and both compile)
|
||||
input_iter p;
|
||||
int shared_mutator_count = 0;
|
||||
value val(shared_mutator_count);
|
||||
input_iter p(val);
|
||||
(*p).mutator();
|
||||
p->mutator();
|
||||
BOOST_TEST_EQ(val.increment_count, 0);
|
||||
BOOST_TEST_EQ(val.private_mutator_count, 0); // mutator() should be invoked on an object returned by value
|
||||
BOOST_TEST_EQ(shared_mutator_count, 2);
|
||||
|
||||
same_type<input_iter::pointer>(p.operator->());
|
||||
}
|
||||
|
||||
{
|
||||
// Test that accessing dereferenced value of a post-incremented iterator works
|
||||
int shared_mutator_count = 0;
|
||||
value val(shared_mutator_count);
|
||||
input_iter p(val);
|
||||
(*p++).mutator();
|
||||
(p++)->mutator();
|
||||
BOOST_TEST_EQ(val.increment_count, 2);
|
||||
BOOST_TEST_EQ(val.private_mutator_count, 0); // mutator() should be invoked on an object returned by value
|
||||
BOOST_TEST_EQ(shared_mutator_count, 2);
|
||||
}
|
||||
|
||||
{
|
||||
int x = 0;
|
||||
iterator_with_proxy_reference i(x);
|
||||
|
Loading…
x
Reference in New Issue
Block a user