What is the past-the-end iterator in STL C++?
The functions begin()
and end()
define a half open range([begin, end)
), which means:
The range includes first element but excludes the last element. Hence, the name past the end.
The advantage of an half open range is:
It avoids special handling for empty ranges. For empty ranges,
begin()
is equal toend()
.It makes the end criterion simple for loops that iterate over the elements: The loops simply
continue as long asend()
is not reached
In what way does end() point to 'one past the end' in non-contiguous containers?
What is
one past the end
supposed to mean for non-contiguous storage?
It means whatever you'd get if you incremented an iterator to the last element.
How does
nullptr
fit the definition of 'one past the end'?
If that's what you get if you increment an iterator to the last element, then it fits the definition.
How is
MyForwardList.begin() == MyForwardList.end()
true if MyForwardList is empty?
For an empty list, they both return the same thing. Likely an "empty" iterator.
Why isn't
end()
always defined asnullptr
?
Because sometimes that's not the most convenient way to define it, and so long as you meet the requirements, you can implement it however you want.
It's basically just a circular definition. The end
function returns whatever you'd get if you took an iterator to the last element in the list and incremented it or, for an empty list, returns the same thing begin
returns. So long as all these relationships hold, everything works, regardless of what internal values or logic you use to guarantee the relationships.
Iterator to one past the last element in list
How can it be known at which positions (in memory) the next elements will be stored?
It is not. Also, using that memory address as (part of) the past-the-end iterator would be incorrect.
Is not:
An iterator is not (necessarily) a pointer. An iterator is not required to store a memory address. What is required is that the de-reference operator be able to calculate a memory address (returned in the form of a reference). Good news, everyone! Applying the de-reference operator to the past-the-end iterator is undefined behavior. So even this reduced requirement is not applicable to the past-the-end iterator. If you are storing an address, go ahead and store whatever you want. (Just be consistent since two past-the-end iterators must compare equal.)
If your iterator does store a pointer (which admittedly is probably common), a simple approach would be to store whatever you would put in the next
field of the last node in the list. This is typically either nullptr
or a pointer to the list's sentinel node.
Would be incorrect:
A std::list
does not invalidate iterators when elements are added to the list. This includes the past-the-end iterator. (See cppreference.com.) If your past-the-end iterator pointed to where the next element would be stored, it would be invalidated by adding that element to the list. Thus, you would fail to meet the iterator invalidation requirements for a std::list
. So not only is storing that address in the past-the-end iterator impossible, it's not allowed.
What happens if you increment an iterator that is equal to the end iterator of an STL container
Following is the quote from Nicolai Josuttis book:
Note that advance() does not check
whether it crosses the end() of a
sequence (it can't check because
iterators in general do not know the
containers on which they operate).
Thus, calling this function might
result in undefined behavior because
calling operator ++ for the end of a
sequence is not defined
In other words, the responsibility of maintaining the iterator within the range lies totally with the caller.
Past-the-end iterator invalidation in C++11
My question is about all containers:
- In C++11, are there any container operations that may invalidate a past-the-end iterator, and where this behavior is ambiguous in the
language specification?
I am not sure what you mean with "where this behavior is ambiguous in the language specification", but there certainly are operations that invalidate past-the-end operators (like insert into a std::vector
or std::string
).
Said differently,
- Can I trust the validity of a past-the-end iterator after performing a container operation that does not say it may invalidate
the past-the-end iterators?
You can trust the past-the-end iterator like any other iterator: Any operation that does not (potentially) invalidate iterators won't invalidate them. Except for the possibility of the standard sporting a bug, that is all operations where it doesn't say that they (potentially) invalidate operators.
Related Topics
Class Type Non-Type Template Parameter Initialization Does Not Compile
Return Value of Operator Overloading in C++
Std::Shared_Ptr Initialization: Make_Shared<Foo>() VS Shared_Ptr<T>(New Foo)
C++ - Arguments for Exceptions Over Return Codes
What Is Return Type of Assignment Operator
The Intersection of Two Sorted Arrays
Creating a Counter That Stays Synchronized Across Mpi Processes
Does the C++ Standard Specify Anything on the Representation of Floating Point Numbers
Capture Output of Spawned Process to String
Understanding the Example on Lvalue-To-Rvalue Conversion
What Is the Easiest Way to Make a C++ Program Crash
Has Anyone Ever Had a Use for the _Counter_ Pre-Processor MACro
Could You Recommend Some Guides About Epoll on Linux
Thread Safe Implementation of Circular Buffer