I read some code written in c++ as following:
#include <algorithm>
#include <iostream>
#include <vector>
using namespace std;
int main() {
int iarr[] = {30, 12, 55, 31, 98, 11, 41, 80, 66, 21};
vector<int> ivector(iarr, iarr + 10);
}
in the above code, I pass iarr and iarr+10 to ivector(iarr, iarr + 10) to create a new vector, is this a proper way to construct a vector? I checked the STL document, it is not mentioned there, is this allowed?
and also, array iarr contains 10 elements, should I use ivector(iarr, iarr+9)?
Yes, it is allowed and yes, you are doing it right.
You are calling this templated constructor:
template<class InputIterator>
vector(
InputIterator _First,
InputIterator _Last
);
The template parameter InputIterator is int* (this is the type of the expressions iarr and iarr + 10).
Since the documentation states that _Last must point to one element beyond the last in the range, the + 10 is also correct to copy all 10 elements in the array (iarr + 9 points to the last element, iarr + 10 points to one beyond the last element).
Simple helper for arrays:
template <typename T, size_t N>
size_t size(T(&)[N]) { return N; }
template <typename T, size_t N>
T* begin(T(&array)[N]) { return array; }
template <typename T, size_t N>
T* end(T(&array)[N]) { return array + N; }
Now you can write:
int main() {
int const arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
std::vector<int> vec(begin(arr), end(arr));
}
And not worry about the size of the array any longer, it'll be computed automatically.
Yes, that is a constructor of std::vector. It's this one:
template <class InputIterator>
vector ( InputIterator first, InputIterator last, const Allocator& = Allocator() );
It takes two iterators (in your case pointers), one to the beginning and another to the end of the sequence of elements that are to initialize the vector. The last parameter is optional, and you don't need it unless you're using custom allocators.
The iterator to the end should be one past the last element you want to include. So, if you want all elements from iarr[0] to iarr[9], you need to pass in iarr + 10.
It is allocating a vector using Iterators and the original iarr[] shown. There are ten elements and +10 is a proper iteration because it is one step past the end. That's how vectors work - it must point to one position past the end. It is copying the contents of the array into the vector. More clearly, it is using this template to create the vector:
template <class InputIterator> vector ( InputIterator first,
InputIterator last, const Allocator& = Allocator() );
Iteration constructor: Iterates
between first and last, setting a copy
of each of the sequence of elements as
the content of the container.
This code is indeed allowed, if you check for example the documentation here
template <class InputIterator>
vector ( InputIterator first, InputIterator last, const Allocator& = Allocator() );
The range specified by the arguments follow the usual convention [first, last[, so passing iarr+10 is correct if you want the whole array to be copied
Yes, this is allowed, and ivector will contain 10 elements. And no, it should not be 9 because the end iterator should be one step past the end. If you know what ranges are, that range would be represented by this range: [beginning, end). That is to say, include the first one, and go right up to but don't include the last one.
Because the STL (C++ standard library cough) is all templates, anything that supports the operators ++ and * (dereference operator) can be passed as iterators to the vector constructor. This property amazingly makes the same code work for both pointers and vector iterators. Standard library design at its best.
Yes, last line in function main() calls constructor of std::vector. Have a look here
to see all vector constructor overloads. The third one is used here. Its parameters are iterators for template type (template argument used here is int and so iterator is of type int*). Second argument is iterator that points to the first sequence element that will not be copied into vector.
Related
I have written a fancy "zip iterator" that already fulfils many roles (can be used in for_each, copy loops, container iterator range constructors etc...).
Under all the template code to work around the pairs/tuples involved, it comes down to the dereference operator of the iterator returning a tuple/pair of references and not a reference to a tuple/pair.
I want my iterator to work with std::sort, so I need to be able to do swap(*iter1, *iter2) and have the underlying values switched in the original containers being iterated over.
The code and a small demo can be viewed here (it's quite a bit to get through): http://coliru.stacked-crooked.com/a/4fe23b4458d2e692
Although libstdc++'s sort uses std::iter_swap which calls swap, e.g. libc++'s does not, and it just calls swap directly, so I would like a solution involving swap as the customization point.
What I have tried (and gotten oooooh so close to working) is instead of returning std::pair/std::tuple from the operator* as I am doing now, is returning a simple wrapper type instead. The intent is to have the wrapper behave as if it were a std::pair/std::tuple, and allow me to write a swap function for it.
It looked like this:
template<typename... ValueTypes>
struct TupleWrapper : public PairOrTuple_t<ValueTypes...>
{
using PairOrTuple_t<ValueTypes...>::operator=;
template<typename... TupleValueTypes>
operator PairOrTuple_t<TupleValueTypes...>() const
{
return static_cast<PairOrTuple_t<ValueTypes...>>(*this);
}
};
template<std::size_t Index, typename... ValueTypes>
decltype(auto) get(TupleWrapper<ValueTypes...>& tupleWrapper)
{
return std::get<Index>(tupleWrapper);
}
template<std::size_t Index, typename... ValueTypes>
decltype(auto) get(TupleWrapper<ValueTypes...>&& tupleWrapper)
{
return std::get<Index>(std::forward<TupleWrapper<ValueTypes...>>(tupleWrapper));
}
template<typename... ValueTypes,
std::size_t... Indices>
void swap(TupleWrapper<ValueTypes...> left,
TupleWrapper<ValueTypes...> right,
const std::index_sequence<Indices...>&)
{
(std::swap(std::get<Indices>(left), std::get<Indices>(right)), ...);
}
template<typename... ValueTypes>
void swap(TupleWrapper<ValueTypes...> left,
TupleWrapper<ValueTypes...> right)
{
swap(left, right, std::make_index_sequence<sizeof...(ValueTypes)>());
}
namespace std
{
template<typename... ValueTypes>
class tuple_size<utility::implementation::TupleWrapper<ValueTypes...>> : public tuple_size<utility::implementation::PairOrTuple_t<ValueTypes...>> {};
template<std::size_t Index, typename... ValueTypes>
class tuple_element<Index, utility::implementation::TupleWrapper<ValueTypes...>> : public tuple_element<Index, utility::implementation::PairOrTuple_t<ValueTypes...>> {};
}
Full code here: http://coliru.stacked-crooked.com/a/951cd639d95af130.
Returning this wrapper in operator* seems to compile (at least on GCC) but produces garbage.
On Clang's libc++, the std::tie fails to compile.
Two questions:
How can I get this to compile with libc++ (the magic seems to lie in the conversion operator of TupleWrapper?)
Why is the result wrong and what did I do wrong?
I know it's a lot of code, but well, I can't get it any shorter as all the tiny examples of swapping tuple wrappers worked fine for me.
1st problem
One of the issues is that the ZipIterator class does not satisfy the requirements of RandomAccessIterator.
std::sort requires RandomAccessIterators as its parameters
RandomAccessIterators must be BidirectionalIterators
BidirectionalIterators must be ForwardIterators
ForwardIterators have the condition that ::reference must be value_type& / const value_type&:
The type std::iterator_traits<It>::reference must be exactly
T& if It satisfies OutputIterator (It is mutable)
const T& otherwise (It is constant)
(where T is the type denoted by std::iterator_traits<It>::value_type)
which ZipIterator currently doesn't implement.
It works fine with std::for_each and similar functions that only require the iterator to satisfy the requirements of InputIterator / OutputIterator.
The reference type for an input iterator that is not also a LegacyForwardIterator does not have to be a reference type: dereferencing an input iterator may return a proxy object or value_type itself by value (as in the case of std::istreambuf_iterator).
tl;dr: ZipIterator can be used as an InputIterator / OutputIterator, but not as a ForwardIterator, which std::sort requires.
2nd problem
As #T.C. pointed out in their comment std::sort is allowed to move values out of the container and then later move them back in.
The type of dereferenced RandomIt must meet the requirements of MoveAssignable and MoveConstructible.
which ZipIterator currently can't handle (it never copies / moves the referenced objects), so something like this doesn't work as expected:
std::vector<std::string> vector_of_strings{"one", "two", "three", "four"};
std::vector<int> vector_of_ints{1, 2, 3, 4};
auto first = zipBegin(vector_of_strings, vector_of_ints);
auto second = first + 1;
// swap two values via a temporary
auto temp = std::move(*first);
*first = std::move(*second);
*second = std::move(temp);
// Result:
/*
two, 2
two, 2
three, 3
four, 4
*/
(test on Godbolt)
Result
Unfortunately it is not possible to create an iterator that produces elements on the fly and can by used as a ForwardIterator with the current standard (for example this question)
You could of course write your own algorithms that only require InputIterators / OutputIterators (or handle your ZipIterator differently)
For example a simple bubble sort: (Godbolt)
template<class It>
void bubble_sort(It begin, It end) {
using std::swap;
int n = std::distance(begin, end);
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {
if (*(begin+j) > *(begin+j+1))
swap(*(begin+j), *(begin+j+1));
}
}
}
Or change the ZipIterator class to satisfy RandomAccessIterator.
I unfortunately can't think of a way that would be possible without putting the tuples into a dynamically allocated structure like an array (which you're probably trying to avoid)
Could anybody explain to me why we can initialize a vector this way?
int a[]={1,2,3,4,5}; std::vector<int> vec(a,a+sizeof(a)/sizeof(int)); I also know this way std::vector<int> vec(5,0); meaning vec has five elements and they are all initialized as 0. But these two ways are not related. How to explain the first one. What is the best way (what most people use) to initialize a vector if we know the values.
Class std::vector has constructor that accepts a pair of input iterators
template <class InputIterator>
vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
And this declaration
std::vector<int> vec(a,a+sizeof(a)/sizeof(int));
includes a call of the constructor where a and a + sizeof(a)/sizeof(int) are two random access iterators because pointers have such category of iterators.
Here a and a + sizeof(a)/sizeof(int) are two pointers that specify a range of initializers taken from the array for the created object of type std::vector<int>.
Take into account that you could also use the constructor that accepts an initializer list. For example
std::vector<int> v { 1, 2, 3, 4, 5 };
This constructor is supported starting from the C++ 2011.
std::vector<int> a = {1,2,3,4,5};
works as of C++11.
See:
http://en.cppreference.com/w/cpp/language/list_initialization
Also you can use:
int a[5]={1,2,3,4,5};
std::vector<int> vec;
for(int i=0; i<5: i++) vec.push_back(a[i]);
While passing iterators into a function, i think it is easier to work with a vectors, so i convert with constructor, but, looks like new vector created. And so, no changes returned from a function. But i need it to be changeble and not take new memory.
template <class Element>
void HeapSort::MaxHeapify(typename vector<Element>::iterator first, typename vector<Element>::iterator last, ...)
{
vector<Element> array(first, last);
...
}
Also, when calling such a function i must explicitely point type of Element. Why?
MaxHeapify<Element>(array.begin(), array.end(), i);
EDIT: with easier i mean that *(first + i) = 5 is not so easy to write and then read as array[i] = 5.
EDIT 2: It is appeared that it can be written as first[i] = 5. May be somebody can me help with second part of the question?
There's no need to construct a new vector inside your function. You can do the algorithm with just the iterators. But if you want to do that because it's "easier", you'll have to copy the values from the resulting array to the parameter range after you're done with the vector. You can't avoid allocating new memory with your approach.
As for the second question, the compiler can't deduce the Element type. You can fix that by declaring the template like this:
template <class Iter>
void HeapSort::MaxHeapify(Iter first, Iter last, ...)
As a bonus, this allows the function to work with any type of random access iterators.
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).
int arr[5] = {0,1,2,3,4};
vector<int> vec;
normally we do:
vector<int> vec(arr, arr + sizeof(arr) / sizeof(int));
but how do initialize a vector vec with only first 3 values of arr?
Also how do I initialize it with the middle 3 values?
I have to initialize it right away, no push_back on multiple lines..
The constructor form you are invoking is this**:
template <class Iterator> vector(Iterator start, Iterator end);
So, you may pass in anything that acts like a pair of iterators. Specifically, you may pass in two pointers, as long as they both point into the same array, and as long as the 2nd doesn't come before the first (if the pointers are equal, they represent an empty range).
In your example, you pass in a pointer to the first element of the array and a pointer to the (mythical) elment-after-the-last-element of the array:
vector<int> vec(arr, arr + sizeof(arr) / sizeof(int));
Generally, you may pass in a pointer to any element as the start, and a pointer past any element as the finish.
but how do I initialize a vector vec with only first 3 values of arr?
Pass in pointers to the first and one past the third elements:
vector<int> vec(arr, arr+3);
Also how do I initialize it with middle 3 values?
Pass in a pointer to the first item you want to copy, and a pointer to one paste the final element. In this case, indexes 1 and 4:
vector<int> vec(arr+1, arr+4);
** Okay, it is slightly more complicated than that, but it's okay to pretend for the sake of this discussion. The actual form is: template <class InputIterator>
vector( InputIterator first, InputIterator last,
const Allocator& alloc = Allocator() );