mirror of
https://github.com/boostorg/iterator.git
synced 2025-05-11 13:33:56 +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
|
// 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>
|
template <class Iterator>
|
||||||
class postfix_increment_dereference_proxy
|
class postfix_increment_proxy
|
||||||
{
|
{
|
||||||
typedef Value value_type;
|
typedef typename iterator_value<Iterator>::type 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)
|
public:
|
||||||
: stored_value(x)
|
explicit postfix_increment_proxy(Iterator const& x)
|
||||||
|
: stored_iterator(x)
|
||||||
|
, stored_value(*x)
|
||||||
{}
|
{}
|
||||||
#endif
|
|
||||||
|
|
||||||
// Returning a mutable reference allows nonsense like
|
// Returning a mutable reference allows nonsense like
|
||||||
// (*r++).mutate(), but it imposes fewer assumptions about the
|
// (*r++).mutate(), but it imposes fewer assumptions about the
|
||||||
// behavior of the value_type. In particular, recall that
|
// behavior of the value_type. In particular, recall that
|
||||||
// (*r).mutate() is legal if operator* returns by value.
|
// (*r).mutate() is legal if operator* returns by value.
|
||||||
// Provides readability of *r++
|
// Provides readability of *r++
|
||||||
operator value_type&() const
|
value_type& operator*() const
|
||||||
{
|
{
|
||||||
return this->stored_value;
|
return 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provides X(r++)
|
// Provides X(r++)
|
||||||
@ -206,9 +177,15 @@ namespace iterators {
|
|||||||
return stored_iterator;
|
return stored_iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provides (r++)->foo()
|
||||||
|
value_type* operator->() const
|
||||||
|
{
|
||||||
|
return boost::addressof(stored_value);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Iterator stored_iterator;
|
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>
|
template <class Iterator>
|
||||||
class writable_postfix_increment_proxy
|
class writable_postfix_increment_proxy
|
||||||
{
|
{
|
||||||
|
typedef typename iterator_value<Iterator>::type value_type;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit writable_postfix_increment_proxy(Iterator const& x)
|
explicit writable_postfix_increment_proxy(Iterator const& x)
|
||||||
: dereference_proxy(x)
|
: dereference_proxy(x)
|
||||||
@ -321,6 +300,12 @@ namespace iterators {
|
|||||||
return dereference_proxy.stored_iterator;
|
return dereference_proxy.stored_iterator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provides (r++)->foo()
|
||||||
|
value_type* operator->() const
|
||||||
|
{
|
||||||
|
return boost::addressof(dereference_proxy.stored_value);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
writable_postfix_increment_dereference_proxy<Iterator> dereference_proxy;
|
writable_postfix_increment_dereference_proxy<Iterator> dereference_proxy;
|
||||||
};
|
};
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include <boost/call_traits.hpp>
|
#include <boost/call_traits.hpp>
|
||||||
#include <boost/polymorphic_cast.hpp>
|
#include <boost/polymorphic_cast.hpp>
|
||||||
#include <boost/type_traits/is_convertible.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
|
// This is a really, really limited test so far. All we're doing
|
||||||
// right now is checking that the postfix++ proxy for single-pass
|
// right now is checking that the postfix++ proxy for single-pass
|
||||||
@ -63,7 +63,23 @@ struct proxy
|
|||||||
|
|
||||||
struct value
|
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
|
struct input_iter
|
||||||
@ -75,21 +91,25 @@ struct input_iter
|
|||||||
>
|
>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
input_iter() {}
|
explicit input_iter(value& val) : state(&val) {}
|
||||||
|
|
||||||
void increment()
|
void increment()
|
||||||
{
|
{
|
||||||
|
++(state->increment_count);
|
||||||
}
|
}
|
||||||
value
|
value
|
||||||
dereference() const
|
dereference() const
|
||||||
{
|
{
|
||||||
return value();
|
return *state;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool equal(input_iter const&) const
|
bool equal(input_iter const&) const
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
value* state;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
@ -198,13 +218,30 @@ int main()
|
|||||||
{
|
{
|
||||||
// test for a fix to http://tinyurl.com/zuohe
|
// test for a fix to http://tinyurl.com/zuohe
|
||||||
// These two lines should be equivalent (and both compile)
|
// 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();
|
||||||
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->());
|
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;
|
int x = 0;
|
||||||
iterator_with_proxy_reference i(x);
|
iterator_with_proxy_reference i(x);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user