According to most C++ references, for instance cplusplus.com, forward iterators are not required to be assignable (I mean, deferenced to an lvalue). However, for several STL algorithms that need to write values, for instance std::fill (also std::generate etc.), the specification uses forward iterator:
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val);
while the equivalent behavior requires lvalue dereference:
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val)
{
while (first != last) {
*first = val;
++first;
}
}
So, it is actually using a mutable forward iterator with a single pass.
Now the questions are:
(1) Why not make it clear that the forward iterators used in these cases are mutable?
(2) Update: I found the following question to be stupid: I temporarily forgot that output iterators do not need to support equality comparison. The above question remains, anyway.
Why use forward iterators instead of output iterators for std::fill, std::generate etc. while they do not actually need multiple passes? (std::copy only needs output iterators, for instance. What's the rationale?)
From the signature
template <class ForwardIterator, class T>
void fill (ForwardIterator first, ForwardIterator last, const T& val);
you cannot infer that ForwardIterator is an iterator described in forward iterator. However, if you read the parameter description, you will find that first and last must be
Forward Iterators to the initial and final positions in a sequence of elements that support being assigned a value of type T.
(emphasis by me). So a forward iterator that fulfills nothing more than what is required of a forward iterator is not a valid argument.
It doesn't seem terribly strange to me, given that the specification for fill is that the (dereferenced) iterator be assignable from T. An output iterator won't suffice because it's not comparable to determine the range end, so a forward_iterator with requirements was chosen.
You'll note that fill_n does use output iterators because no iterator comparison is needed to determine the end of the sequence to fill.
Related
This program:
#include <ranges>
#include <numeric>
#include <iostream>
int main() {
auto rng = std::ranges::istream_view<int>(std::cin);
std::cout << std::accumulate(std::ranges::begin(rng), std::ranges::end(rng), 0);
}
is supposed to sum up all integers appearing as text on the standard input stream. But - it doesn't compile. I know std::ranges::begin() and std::ranges::end() exist, so what's going on? The compiler tells me it can't find a suitable candidate; why?
From inception up through C++17, everything in <algorithm> is based on iterator pairs: you have one iterator referring to the beginning of a range and one iterator referring to the end of the range, always having the same type.
In C++20, this was generalized. A range is now denoted by an iterator and a sentinel for that iterator - where the sentinel itself need not actually be an iterator of any kind, it just needs to be a type that can compare equal to its corresponding iterator (this is the sentinel_for concept).
C++17 ranges tend to be† valid C++20 ranges, but not necessarily in the opposite direction. One reason is the ability to have a distinct sentinel type, but there are others, which also play into this question (see below).
To go along with the new model, C++20 added a large amount of algorithms into the std::ranges namespace that take an iterator and a sentinel, rather than two iterators. So for instance, while we've always had:
template<class InputIterator, class T>
constexpr InputIterator find(InputIterator first, InputIterator last,
const T& value);
we now also have:
namespace ranges {
template<input_iterator I, sentinel_for<I> S, class T, class Proj = identity>
requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
constexpr I find(I first, S last, const T& value, Proj proj = {});
template<input_range R, class T, class Proj = identity>
requires indirect_binary_predicate<ranges::equal_to,
projected<iterator_t<R>, Proj>, const T*>
constexpr borrowed_iterator_t<R>
find(R&& r, const T& value, Proj proj = {});
}
The first overload here takes an iterator/sentinel pair and the second takes a range instead.
While a lot of algorithms added corresponding overloads into std::ranges, the ones in <numeric> were left out. There is a std::accumulate but there is no std::ranges::accumulate. As such, the only version we have available at the moment is one that takes an iterator-pair. Otherwise, you could just write:
auto rng = std::ranges::istream_view<int>(std::cin);
std::cout << std::ranges::accumulate(rng, 0);
Unfortunately, std::ranges::istream_view is one of the new, C++20 ranges whose sentinel type differs from its iterator type, so you cannot pass rng.begin() and rng.end() into std::accumulate either.
This leaves you with two options generally (three, if you include waiting for C++23, which will hopefully have a std::ranges::fold):
Write your own range-based and iterator-sentinel-based algorithms. Which for fold is very easy to do.
Or
There is a utility to wrap a C++20 range into a C++17-compatible one: views::common. So you could this:
auto rng = std::ranges::istream_view<int>(ints) | std::views::common;
std::cout << std::accumulate(rng.begin(), rng.end(), 0);
Except not in this specific case.
istream_view's iterators aren't copyable, and in C++17 all iterators must be. So there isn't really a way to provide C++17-compatible iterators based on istream_view. You need proper C++20-range support. The future std::ranges::fold will support move-only views and move-only iterators, but std::accumulate never can.
Which in this case, just leaves option 1.
†A C++20 iterator needs to be default-constructible, which was not a requirement of C++17 iterators. So a C++17 range with non-default-constructible iterators would not be a valid C++20 range.
The problem is that the end of a C++ range is not, in the general case, an iterator, but rather, a sentinel. A sentinel can have a different type than an iterator and admit fewer operations - as, generally speaking, you mostly need to compare against it to know you've reached the end of the range, and may not be allowed to just work with it like any iterator. For more about this distinction, read:
What's the difference between a sentinel and an end iterator?
Now, standard-library algorithms (including the ones in <numeric>) take pairs of iterators of the same type. In your example:
template< class InputIt, class T >
constexpr T accumulate( InputIt first, InputIt last, T init );
see? InputIt must be the type of both the beginning and end of the range. This can (probably) not even be "fixed" for the istream_view range, because the end of the standard input really isn't an iterator per se. (Although maybe you could bludgeon it into being an iterator and throw exceptions when doing irrelevant things with it.)
So, we would need either a new variant of std::accumulate or an std::ranges::accumulate, which we currently don't have. Or, of course, you could write that yourself, which should not be too difficult.
Edit: One last option, suggested by #RemyLebeau, is to use an std::istream_iterator instead:
std::cout << std::accumulate(
std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
0);
I want to determine the std container type (at least associative or linear) from an iterator passed to a function.
I have searched for appropriate iterator traits and type traits without success.
template <typename RangeIter, typename InputIter>
inline bool filter(RangeIter in_data, InputIter result)
{
...
/* determine the container types here - but how?!? */
std::copy_if(in_data.first, in_data.second, result, /* some predicate code here*/);
...
}
No. (ok, that might be a bit too short for StackOverflow).
There's no way to determine the "container" that an iterator refers to, because they need not refer to a container at all.
Example:
int foo, bar;
std::copy(&foo, &foo+1, &bar);
In this example, none of the iterators passed to std::copy refer to a "container".
(This question has the same "question template" as Why does std::max_element require a ForwardIterator? but the answer has to be different, because std::fill doesn't return an iterator into the output sequence.)
std::fill is defined to work only when the output range is given by a pair of ForwardIterators. However, the superficially similar std::fill_n works fine with OutputIterator.
Surely the algorithm is simply
template<class OutIt, class T>
void fill(OutIt first, OutIt last, T value)
{
while (first != last) {
*first = value;
++first;
}
}
What about this algorithm requires ForwardIterator? What am I missing?
Output iterators are not comparable. The == and != operators are not defined for output iterators.
You need a forward iterator, because it
satisfies the requirements of an input iterator
which supports EqualityComparable.
With output iterators, std::fill cannot compare first with last:
while (first != last) {
Not supported, for output iterators.
std::fill_n avoids this comparison, it just uses the counter to write to the iterator, so all it needs is an output iterator.
There is no notion of "range" for OutputIterator. Thus you must provide a repetition count. With ForwardIterator you can work over a range of elements.
Those are semantically different things. fill_n is for adding some elements starting from a supplied iterator. fill is for changing a range of elements.
Take inserters (e.g. a back_inserter) for an example. You can insert more elements than there is in the container, so last doesn't even make sense.
OutputIterator gives you a place where you can throw in an object. ForwardIterator objects must exists.
From cppreference:
The only valid use of operator* with an output iterator is on the left of an assignment: operator* may return a proxy object, which defines a member operator= (which may be a template)
This means you basically cannot read from it, which is in the contrast to the ForwardIterator:
A ForwardIterator is an Iterator that can read data from the pointed-to element.
ISO C++11 24.3:
template <class InputIterator, class Distance>
void advance(InputIterator& i, Distance n);
// ...
template <class ForwardIterator>
ForwardIterator next
(
ForwardIterator x,
typename std::iterator_traits<ForwardIterator>::difference_type n = 1
);
Why std::next does not accept InputIterators?
One of legal use cases I am thinking about is:
first = find(next(first, x), last, 11); // ...
I have found appropriate DR:
next/prev return an incremented iterator without changing the value of the original iterator. However, even this may invalidate an InputIterator. A ForwardIterator is required to guarantee the 'multipass' property.
But I don't understand how multipass/invalidation is related to that. Using same multipass/invalidation reasoning, we can even ban std::find for InputIterators:
template<class InputIterator, class T>
InputIterator find(InputIterator first, InputIterator last, const T& value);
There is nothing special about std::next in compare to std::find or std::vector::insert(pos, first, last) which have perfectly legal use cases for InputIterators
Moreover std::next(it, n) can be used in generic code, which operates not only on InputIterators.
In effect, input iterators cannot be usefully copied, because once an input iterator is incremented, any copy left lying around is invalidated.
std::next takes an iterator and returns another iterator which has been advanced n times. You can't do that with an input iterator without invalidating the original iterator, which makes std::next pointless. By constrast, std::advance advances the specified iterator n times, which is fine with an input iterator.
std::next is the iterator generalization of operator+(T*, size_t). std::advance is the iterator generalization of operator+=(T*&, size_t). It may well be that std::advance, like operator+=, should return a reference instead of void.
It's true that there is a similar issue with std::find (and related functions); they, too, will invalidate any copy of the specified input iterators. But it is quite possible that the committee found that issue less serious.
I'm trying to add some debug instrumentation for a vector. My class "has a" vector and offers functions such as:
template <typename InputIterator>
void assign(InputIterator first, InputIterator last)
Vector and strings are containers with contiguous memory. When first and last are from a vector (or other container with contiguous memory), I can perform additional sanity checks on the iterators. For example, I can check:
last > first
[first, last) don't overlap with existing elements
count = last - first + 1 is sane
I want to provide a specialization for the additional diagnostics and instrumentation when the container uses contiguous memory, but I don't know what the iterator is called (and have not been able to locate it grepping through sources):
template <typename SequentialIterator>
void assign(SequentialIterator first, SequentialIterator last)
What is the name of the 'SequentialIterator' or 'ContiguousIterator'?
You could use tag dispatching and some standard type traits to choose the appropriate implementation of assign() based on the category of the iterator.
For instance, this basic solution lets you provide two different implementations for random access iterators and non-random access iterators:
#include <type_traits>
#include <iterator>
struct X
{
template <typename InputIterator>
void assign(InputIterator first, InputIterator last)
{
assign_impl(
first, last,
typename std::iterator_traits<InputIterator>::iterator_category()
);
}
template <typename InputIterator>
void assign_impl(InputIterator first, InputIterator last,
std::random_access_iterator_tag)
{
// Implementation for random access iterator...
}
template <typename InputIterator>
void assign_impl(InputIterator first, InputIterator last,
std::input_iterator_tag)
{
// Implementation for non-random access iterator...
}
};
There is no guarantee that the elements of the sequence under a particular iterator are contiguous. There are only guarantees on the operations you can perform with an iterator. There are four main iterator types:
Random Access
Bidirectional
Forward
Input
They can each (except Input) also satisfy the requirements of an Output Iterator, which makes them mutable iterators.
The closest iterator to what you're asking for is a Random Access Iterator. It supports comparison with > and < and allows you to add and subtract iterators from each other. You can even use the array subscript operator with them. They give the illusion that the elements are stored contiguously, but there's no guarantee that they really are.