Is regular overwrite copy mode undefined for Container iterators? - c++

The following code compiles just fine, overwriting the values in v2 with those from v1:
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = {6, 7, 8, 9, 10};
std::copy(v1.begin(), v1.end(), v2.begin());
The third argument of std::copy is an OutputIterator. However, the Container requirements specify that a.begin(), where a is a Container object, should have a return type of iterator which is defined as:
any iterator category that meets the forward iterator requirements.
Forward iterator requirements do not include the requirements of output iterators, so is the example above undefined? I'm using the iterator as an output iterator even though there's no obvious guarantee that it will be one.
I'm fairly certain the above code is valid, however, so my guess is that you can infer from the details about containers that the forward iterator returned by begin() will in fact also support the output iterator requirements. In that case, when does begin() not return an output iterator? Only when the container is const or are there other situations?

Forward iterators can conform the the specifications of an output iterator if they're mutable, depending on the type of the sequence. It's not explicitly spelled out (unlike the fact that they to input iterator requirements), but if we take a look at the requirements table
we can go and check if a given forward iterator conforms to them:
*r = o
(§24.2.5/1): if X is a mutable iterator, reference is a reference to T
A mutable reference is assignable (unless you have a non-assignable type, obviously).
++r, r++, *r++ = o
(§24.2.5 Table 109)
The first line in Table 109 is the same requirement as for output iterators, except that forward iterators don't have the remark. The second line is more restrictive than for output iterators, since it specifies that a reference must be returned.
Bottom line, if you have a mutable forward iterator into a sequence of copy-assignable types, you have a valid output iterator.
(Technically, a constant iterator into a sequence of types that have a operator=(...) const and mutable members would also qualify, but let's hope nobody does something like that.)

Forward iterator requirements do not include the requirements of output iterators
This sounds backwards. OutputIterators need to satisfy fewer criteria than ForwardIterators.
(Forward iterators should be reusable after increment, i.e. incrementing them twice should yield the same result).
Therefore, it is ok provided that the output iterator stays valid until the algorithm completes. IOW:
auto outit = std::begin(v2);
std::advance(outit, v1.size()); // or: std::distance(std::begin(v1), std::end(v2))
// outit should still be valid here
Edit To the comment:
§ 24.2.1
Iterators that further satisfy the requirements of output iterators are called mutable iterators. Nonmutable iterators are referred to as constant iterators.
Now, let me find the bit that ties this together saying vector::begin() returns mutable Random Access iterator.
For info
§ 24.2.5 Forward iterators [forward.iterators]
1 A class or pointer type X satisfies the requirements of a forward iterator if
X satisfies the requirements of an input iterator (24.2.3),
X satisfies the DefaultConstructible requirements (17.6.3.1),
if X is a mutable iterator, reference is a reference to T; if X is a const iterator, reference is a reference to const T,
the expressions in Table 109 are valid and have the indicated semantics, and
objects of type X offer the multi-pass guarantee, described below.

Related

C++ using default-initialized iterators to create an empty std::string

Is the following code valid and well-defined?
auto start = std::string::const_iterator();
auto end = std::string::const_iterator();
auto output = std::string(start, end);
(The expected output being an empty string.)
According to cppreference.com, a random access iterator, of which a string iterator is one, meets all requirements of a bidirectional iterator.
Furthermore, a bidirectional iterator meets all requirements of a forward iterator.
Finally, since C++14, a forward iterator can be value-initialized, and will compare equal to all other value-initialized forward iterators of the same type:
A value-initialized LegacyForwardIterator behaves like the
past-the-end iterator of some unspecified empty container: it compares
equal to all value-initialized LegacyForwardIterators of the same
type.
Based on that, I believe this is well-defined at least as of C++14.

Requirements for forward iterator iterator_traits::reference

I'm trying to figure out exactly what requirements are made on forward_iterators' reference types. In the obvious cases you'll have value_type = T; and reference = T&;. Reading the cppreference page on forward iterator requirements, I saw
Expression Return Equivalent expression
*i++ reference value_type& temp=*i; ++i; return temp;
std::vector<bool> shows that the "equivalent expression" isn't always valid since it returns a proxy object:
std::vector<bool> v(10);
auto i = v.begin();
std::vector<bool>::iterator::value_type& temp = *i; // error
// can't bind bool& to std::_Bit_reference
The equivalent expression isn't mentioned in the standard that I saw. The proxy object allows assignment though, which might be the key to conformance.
Outside of just all around trying to nail down the requirements, my specific question concerns knowing whether or not having value_type == reference where neither is a reference or supports assignment, would work with the standard libraries.
Would some Container<int> with an iterator tagged as forward_iterator_tag and reference == int be valid?
The requirements are enumerated in [forward.iterators]:
A class or pointer type X satisfies the requirements of a forward iterator if
X satisfies the requirements of an input iterator (24.2.3),
X satisfies the DefaultConstructible requirements (17.6.3.1),
if X is a mutable iterator, reference is a reference to T; if X is a const iterator, reference is a reference
to const T,
[...]
So if your container has reference == int, then it does not meet the requirements of forward iterator. Which I suppose technically makes vector<bool>::iterator just an input iterator, even though it's tagged as a random access iterator.

const to non-const iterator comparisons, are they valid [duplicate]

This question already has answers here:
Is comparison of const_iterator with iterator well-defined?
(3 answers)
Closed 4 years ago.
I have two iterators into a container, one const and one non-const. Is there an issue with comparing them to see if they both refer to the same object in the container? This is a general C++11 iterator question:
Can a const and non-const iterator be legitimately compared to see if
they both refer to the same object, independent of the type of
container (i.e., they are both iterators that are guaranteed to refer
to objects in the same container or that container's end(), but one is
const and the other is not)?
For example, consider the following code:
some_c++11_container container;
// Populate container
...
some_c++11_container::iterator iObject1=container.begin();
some_c++11_container::const_iterator ciObject2=container.cbegin();
// Some operations that move iObject1 and ciObject2 around the container
...
if (ciObject2==iObject1) // Is this comparison allowed by the C++11 standard?
...; //Perform some action contingent on the equality of the two iterators
Yes, this will work like you expect.
The Standard guarantees that for any container type, some_container::iterator can be implicitly converted to some_container::const_iterator.
The first table in 23.2.1 [container.requirements.general], after defining X as a container type which contains objects of type T, has:
Expression: X::iterator
Return type: iterator type whose value type is T
Note: any iterator category that meets the forward iterator requirements. convertible to X::const_iterator.
Expression: X::const_iterator
Return type: constant iterator type whose value type is T
Note: any iterator category that meets the forward iterator requirements.
(These aren't really expressions, and are types, rather than having "return types", but that's how they're squeezed into the table that is mostly expressions.)
So when you have ciObject2==iObject1, the compiler notices that the best operator== is ciObject2==some_container::const_iterator(iObject1). And operator== on two const_iterator tells you if they refer to the same element.
(I don't see anything explicitly saying that the result of this conversion refers to the same object as the original iterator. I guess that's just understood.)
From §24.2.3/1
A class or pointer type X satisfies the requirements of an input iterator for the value type T if X satisfies the Iterator (24.2.2) and EqualityComparable (Table 17) requirements ...
Thus input iterators are required to be EqualityComparable.
All standard library container iterators must satisfy forward iterator requirements (§23.2.1 - Table 96). Since those requirements are a superset of input iterator requirements, it follows these iterators must satisfy the EqualityComparable concept.
Also, from §23.2.1 - Table 96, X::iterator is required to be convertible to
X::const_iterator.
Adding the two together answers your question that it is indeed required by the standard that comparing a Container::const_iterator to a Container::iterator is well-defined (as long as both are valid iterators pointing to the same container).
I don't think it is possible for there to be an issue comparing them. If you have a const iterator when you check for it being the end the iterator end() returns is not const.
IIRC iterator implicitly converts to const_iterator. And the result must point to the same position.
If so the mixed compare will do the conversion then compare the now compatible const_iterators.

Does incrementing a mutable input iterator invalidate old iterator values?

Iterators that further satisfy the requirements of output iterators are called mutable iterators. Nonmutable iterators are referred to as constant iterators. [24.2.1:4]
This suggests you could have a mutable input iterator, which meets the requirements of both input and output iterators.
After incrementing an input iterator, copies of its old value need not be dereferenceable [24.2.3]. However, the standard does not say the same for output iterators; in fact, the operational semantics for postfix increment are given as { X tmp = r; ++r; return tmp; }, suggesting that output iterators may not invalidate (copies of) old iterator values.
So, can incrementing a mutable input iterator invalidate old iterator copies?
If so, how would you support code like X a(r++); *a = t or X::reference p(*r++); p = t with (e.g.) a proxy object?
If not, then why does boost::iterator claim it needs a proxy object? (Link is code; scroll down to read the comments on structs writable_postfix_increment_proxy and postfix_increment_result). That is, if you can return a (dereferenceable) copy of the old iterator value, why would you need to wrap this copy in a proxy?
The explanation if found in the next section, [24.2.5] Forward iterators, where it is stated how these differ from input and output iterators:
Two dereferenceable iterators a and b of type X offer the multi-pass guarantee if:
— a == b implies ++a == ++b and
— X is a pointer type or the expression (void)++X(a), *a is equivalent to the expression *a.
[ Note: The requirement that a == b implies ++a == ++b (which is not true for input and output iterators) and the removal of the restrictions on the number of the assignments through a mutable iterator (which applies to output iterators) allows the use of multi-pass one-directional algorithms with forward iterators.
—end note ]
Unfortunately, the standard must be read as a whole, and the explanation is not always where you expect it to be.
Input and output iterators are basically designed to allow single-pass traversal: to describe sequences where each element can only be visited once.
Streams are a great example. If you read from stdin or a socket, or write to a file, then there is only the stream's current position. All other iterators pointing to the same underlying sequence are invalidated when you increment an iterator.
Forward iterators allow multi-pass traversal, the additional guarantee you need: they ensure that you can copy your iterator, increment the original, and the copy will still point to the old position, so you can iterate from there.

Does std::vector::swap invalidate iterators?

If I swap two vectors, will their iterators remain valid, now just pointing to the "other" container, or will the iterator be invalidated?
That is, given:
using namespace std;
vector<int> x(42, 42);
vector<int> y;
vector<int>::iterator a = x.begin();
vector<int>::iterator b = x.end();
x.swap(y);
// a and b still valid? Pointing to x or y?
It seems the std mentions nothing about this:
[n3092 - 23.3.6.2]
void swap(vector<T,Allocator>& x);
Effects:
Exchanges the contents and capacity()
of *this with that of x.
Note that since I'm on VS 2005 I'm also interested in the effects of iterator debug checks etc. (_SECURE_SCL)
The behavior of swap has been clarified considerably in C++11, in large part to permit the Standard Library algorithms to use argument dependent lookup (ADL) to find swap functions for user-defined types. C++11 adds a swappable concept (C++11 §17.6.3.2[swappable.requirements]) to make this legal (and required).
The text in the C++11 language standard that addresses your question is the following text from the container requirements (§23.2.1[container.requirements.general]/8), which defines the behavior of the swap member function of a container:
Every iterator referring to an element in one container before the swap shall refer to the same element in the other container after the swap.
It is unspecified whether an iterator with value a.end() before the swap will have value b.end() after the swap.
In your example, a is guaranteed to be valid after the swap, but b is not because it is an end iterator. The reason end iterators are not guaranteed to be valid is explained in a note at §23.2.1/10:
[Note: the end() iterator does not refer to any element, so it may be
invalidated. --end note]
This is the same behavior that is defined in C++03, just substantially clarified. The original language from C++03 is at C++03 §23.1/10:
no swap() function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped.
It's not immediately obvious in the original text, but the phrase "to the elements of the containers" is extremely important, because end() iterators do not point to elements.
Swapping two vectors does not invalidate the iterators, pointers, and references to its elements (C++03, 23.1.11).
Typically the iterator would contain knowledge of its container, and the swap operation maintains this for a given iterator.
In VC++ 10 the vector container is managed using this structure in <xutility>, for example:
struct _Container_proxy
{ // store head of iterator chain and back pointer
_Container_proxy()
: _Mycont(0), _Myfirstiter(0)
{ // construct from pointers
}
const _Container_base12 *_Mycont;
_Iterator_base12 *_Myfirstiter;
};
All iterators that refer to the elements of the containers remain valid
As for Visual Studio 2005, I have just tested it.
I think it should always work, as the vector::swap function even contains an explicit step to swap everything:
// vector-header
void swap(_Myt& _Right)
{ // exchange contents with _Right
if (this->_Alval == _Right._Alval)
{ // same allocator, swap control information
#if _HAS_ITERATOR_DEBUGGING
this->_Swap_all(_Right);
#endif /* _HAS_ITERATOR_DEBUGGING */
...
The iterators point to their original elements in the now-swapped vector object. (I.e. w/rg to the OP, they first pointed to elements in x, after the swap they point to elements in y.)
Note that in the n3092 draft the requirement is laid out in §23.2.1/9 :
Every iterator referring to an
element in one container before the
swap shall refer to the same element
in the other container after the swap.