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]);
Related
I'm relatively new to c++ and I've tried to do some research, but while searching online I've mainly come across C arrays rather than std::array. What are the most efficient ways to append std::array elements into a std::vector, and to insert std::array elements into a std::vector? Should I use STL functions such as std::copy? I'm currently using C++17, MinGW64.
To append the elements of an existing array (or other range in general) to a vector you can just use the vector's insert overload for an iterator range:
vector<int> vec{1, 2, 3};
array<int, 3> arr{4, 5, 6};
// arr could be some other container or bare array as well, for ex.:
// int arr[] = {4, 5, 6};
// vector<int> arr {4, 5, 6};
// list<int> arr {4, 5, 6};
// ...
vec.insert(vec.end(), begin(arr), end(arr)); // insert at vec.end() = append
//or vec.insert(vec.end(), arr.begin(), arr.end()); // insert at vec.end() = append
Note that if you have some other type instead of int which is expensive to copy, and you want to move the elements from the source array, you can use
move_iterator, for ex.
vec.insert(vec.end(), move_iterator(arr.begin()), move_iterator(arr.end()));
For operations on ranges in general, the container member functions are to be preferred instead of the same-named functions from the <algorithm> header.
So for example in this case the vec.insert will insert the range at once, as opposed if have had used the std::insert where the elements would be inserted one by one.
This is very nice explained in the Scott Meyers Effective STL.
Live
"Appending" values can mean two things - you either want to copy/duplicate the elements in question, or you don't need them in the source container aftwards and might also move them into the destination container. Also, it makes sense to distinguish between insertion at construction time vs. appending to an existing, already constructed container: if you can, always construct a container with the elements that it's supposed to own.
Copy-append std::array elements to an already constructed std::vector:
std::vector<T> dest;
std::array<T, N> source;
// ...
dest.insert(dest.end(), source.cbegin(), source.cend());
Move-append std::arrayelements to an already constructed std::vector.
std::vector<T> dest;
std::array<T, N> source;
// ...
dest.insert(dest.end(), std::move_iterator(source.begin()),
std::move_iterator(source.end()));
Copy std::array elements into a std::vector at construction:
std::array<T, N> source;
// ...
std::vector<T> dest(source.cbegin(), source.cend());
Move std::array elements into a std::vector at construction:
std::array<T, N> source;
// ...
std::vector<T> dest(std::move_iterator(source.begin()),
std::move_iterator(source.cend()));
There is not much to add here when talking about insertion into the middle - the only notable difference is that it will always be less efficient, as the remaining elements in the destination std::vector will be move-constructed (which is O(N)).
Note also that for appending elements, there is std::move and std::copy from the <algorithm> header (where std::copy can be used with std::move_iterators). However, these cannot be as efficient as a direct call to std::vector::insert, because the former operates on the iterator abstraction and processes the copy/move one element at a time without knowing about the storage details of the destination (this can result in multiple buffer resizings), while the latter is a std::vector member function and will resize the buffer only once (if required).
There are so many alternative ways of addressing elements of a vector.
I could use a pointer like so:
vector<int> v = {10, 11, 12};
int *p = &v[0];
cout << *p; //Outputs "10"
I could use a pointer this way too:
vector<int> v = {10, 11, 12};
vector<int>::pointer p = v.data();
cout << *p; //Outputs "10"
I could also use the iterator type:
vector<int> v = {10, 11, 12};
vector<int>::iterator i = v.begin();
cout << *i; //Outputs "10"
Are there any significant differences that I'm missing here?
As far as being able to perform the task at hand, they all work equally well. After all, they all provide an object which meets the requirements of an iterator and you are using them to point at the same element of the vector. However, I would pick the vector<int>::iterator option because the type is more expressive about how we intend to use it.
The raw pointer type, int*, tells you very little about what p is, except that it stores the address of an int. If you think about p in isolation, its type doesn't tell you very much about how you can use it. The vector<int>::pointer option has the same issue - it just expresses the type of the objects it points at as being the element type of a vector. There's no reason it actually needs to point into a vector.
On the other hand vector<int>::iterator tells you everything you need to know. It explicitly states that the object is an iterator and that iterator is used to point at elements in a vector<int>.
This also has the benefit of being more easily maintainable if you ever happen to change the container type. If you changed to a std::list, for example, the pointer type just wouldn't work any more because the elements are not stored as a contiguous array. The iterator type of a container always provides you with a type you can use to iterate over its elements.
When we have Concepts, I'd expect the best practise to be something like:
ForwardIteratorOf<int> it = std::begin(v);
where ForwardIteratorOf<int> (which I am imagining exists) is changed to whatever concept best describes your intentions for it. If the type of the elements doesn't matter, then just ForwardIterator (or BidirectionalIterator, RandomAccessIterator, or whatever).
If you add the check:
if ( !v.empty() )
Then, all the example you've shown are equally valid.
If you are about to iterate over the elements of the vector, I would go with:
vector<int>::iterator i = v.begin();
It's easier to check whether the iterator has reached the end of the vector with an iterator than with the other forms.
if ( i != v.end() )
{
// Do stuff.
}
All these ways have their advantages, but at the core they are very similar. Some of them don't work though (they cause so-called "undefined behaviour") when the vector is empty.
According to cppreference:
A pointer to an element of an array satisfies all requirements of LegacyContiguousIterator
which is the most powerful iterator as it encompasses all other iterators functionality. So they can be one and the same, an iterator is just a means of making our code clear, consice and portable.
For example we could have some container "C"...
//template <typename T, int N> class C { //for static allocation
template <typename T> class C {
//T _data[N]; //for static allocation
T* _data; //need to dynamically allocate _data
public:
typedef T* iterator;
}
where C<int>::iterator would be an int* and there would be no difference.
Maybe we don't want/need the full power of a LegacyContiguousIterator so we could redefine C<int>::iterator
as another class that follows the outline for say LegacyForwardIterator. This new iterator class may redefine operator*. In this case it is implementation dependant and an int* may cause undefined behaviour when trying to access the elements.
This is why iterators should be preferred but in most cases they are going to be the same thing.
In both cases our container " C" will work just like other STL containers so long as we define all the other necessary member functions and typedefs.
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() );
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.