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.
Related
(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.
Suppose I write template function that processes a range of elements.
template <typename Iter>
void func(Iter first, Iter last);
In this function I want to call some low-level c-function that expects to get contiguous buffer and it's size. Generic way to do this stuff is to copy my range to contiguous container and then call c-function.
template <typename Iter>
void func(Iter first, Iter last)
{
typedef typename iterator_traits<Iter>::value_type value_type;
vector<value_type> buf(first, last);
c_func((void*) buf.data(), buf.size() * sizeof(value_type));
}
But if iterators are already point to some contiguous memory space extra copying will be performed.
So the question is, is there a way to determine are iterators point to contiguous memory space and if it is how can I specialize my function for this case.
At the moment there is no direct way to determine if an iterator is used for contiguous memory. There is a proposal to add an iterator refinement (see e.g. n3884) but without something like that and a way to detect the property being added for the different iterators you'll have a hard time determining this trait.
The key problem requiring implementer support is that the name if iterator types is not specified. As a result, you can't create a traits applicable to all known to be contiguous iterator types. Since the iterator types for std::vector<T> and std::array<T> can be identical and T* I don't think you can even create a portable specialization.
What you could do, however, is to test in a trait whether an iterator type matches one of the known iterator types for its value type. For example:
template <typename T, typename It>
struct test_iterator
: std::integral_constant<bool,
std::is_same<T*, It>::value
|| std::is_same<typename std::vector<T>::iterator, It>::value
|| std::is_same<std::string::iterator, It>::value
|| std::is_same<std::wstring::iterator, It>::value
> {
};
template <typename It>
struct is_contiguous
: std::integral_constant<bool,
test_iterator<typename std::iterator_traits<It>::value_type,
It>::value> {
};
[I haven't tried to compile the code, i.e., it is probably littered with small typos; the general approach should work, though]
To add, e.g., std::array<T, N> you'd need some way to figure out the dimension in a static way. I guess, that won't be viable. It may also be necessary to test the various const_iterators.
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 am not crystal clear about pointer and iterator, especially in the case of constructing vector from an array.
From the description about vector, it says
// the iterator constructor can also be used to construct from arrays:
int myints[] = {16,2,77,29};
std::vector<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );
in primary, it is:
#include <vector>
int arr[ARR_LEN] = { /* Array elements */ };
std::vector<int> vecInt(arr, arr + ARR_LEN);
does it use the following constructor?
template <class InputIterator>
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
If so, then here, is a pointer (for array) treated into an random access iterator, then as input iterator? How does the compiler do this? Considering iterators from other containers, what is the type of this iterator, e.g. "array[int]"::iterator?
int arr[N] = {};
for (int i= 0; i<N; i++) {}
instead of the above, can I do something like as follows?
for (ItorType it=arr; it!=arr+N; ++it) { ... }
The iterator template arguments are not specific class. In fact, the names have no real impact on the compilation process. Instead, they refer to specific concepts which specify the methods available for a specific iterator. Any RandomAccessIterator also has to be an InputIterator: the RandomAccessIterator concept is a refinement of the InputIterator concept.
The compiler really just passes these arguments, in your case pointers, through. Pointers implement the RandomAccessIterator concept, i.e., they can be readily used with the std::vector<...> constructor you quoted. BTW, the proper way to write this initialization is
std::vector<int> vecInt(std::begin(arr), std::end(arr));
The function templates begin() and end() were added to the latest version of C++ and deduce the size of statically sized arrays. Both approaches used to determine the size in your example are problematic in some ways.
does it use the following constructor?
Yes.
If so, then here, is a pointer (for array) treated into an random access iterator, then as input iterator?
Yes.
How does the compiler do this?
Concepts like InputIterator are informal specifications of how a type must behave; to be an InputIterator, it must support indirection (*it) to access a value, and increment (++it and it++) to move to the next value. To be a RandomAccessIterator, it must support these operations, plus a few more such as addition and subtraction. Pointers meet these requirements, so can be used wherever an iterator is required.
Considering iterators from other containers, what is the type of this iterator, e.g. "array[int]"::iterator?
You'd use a pointer, int*, to iterate over an array, int[]. When used as a function argument (as here), or used in arithmetic (such as arr+N), the array is automatically converted to a pointer to its first element.
instead of the above, can I do something like as follows?
Yes. ItorType would be the corresponding pointer type, int*. In C++11, we have begin and end functions to get the beginning and end of any range including arrays:
for (auto it = std::begin(arr); it != std::end(arr); ++it)
and also range-based loops:
for (int i : arr)
Yes, it uses aforementioned constructor and treats pointer as random access iterator. Since all RandomAccessIterators also are InputIterators (see here). Compiler can do this cause Iterator classes defines valid operators on itself in terms of static interfaces. No matter how they implemented and actually means. Only type correctness and interface correctness plays their roles.
You could typedef int * ItorType and your last sample would compiled and worked very well.
template <class InputIterator>
vector (InputIterator first, InputIterator last,
const allocator_type& alloc = allocator_type());
The compiler will use this constructor simply because it a two argument constructor with the same type for both arguments.
It doesn't have to be that the arguments are iterators. For instance if you write
vector<int> x(3.0, 1.0);
the compiler will try to use the same constructor (which would then lead to compile errors because doubles can't be used like iterators).
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.