I'm just looking through std::vector constructors and seeing some people use the constructor that takes 2 iterators and just use it with arrays like:
int arr[5] = [1,2,3,4,5]
std::vector<int> v(arr, arr arr.size()/arr[0])
Why does this work? So is an array just an iterator as well? Also one more question here:
int a[5] = {0, 1, 2, 3, 4};
vector<int> v4(a, *(&a 1));
What exactly is this doing and why is it different from the other thing above?
CodePudding user response:
int arr[5] = [1,2,3,4,5] std::vector<int> v(arr, arr arr.size()/arr[0])
Why does this work?
This doesn't work.
So is an array just an iterator as well?
An array is not an iterator. But an array can implicitly convert to a pointer to first element which is an iterator for the array.
int a[5] = {0, 1, 2, 3, 4}; vector<int> v4(a, *(&a 1));
What exactly is this doing
This is an unnecessarily complex way of writing:
std::vector<int> v4(std::begin(a), std::end(a));
CodePudding user response:
int arr[5];
int *begin=arr;
int *end=arr sizeof(arr)/sizeof(*arr);
std::vector<int> v(begin, end);
Here arr
(as in begin=arr) is a "pointer", which can be used like an iterator. So begin=arr
is an "iterator" to the beginning of the array and end
is an "iterator" to the "beginning 5".
To be a little more precise: Here the name of the array "decays" into a pointer. In many circumstances the name can be used as a pointer and would be similar to &arr[0]
, i.e. a pointer to the first element.
The construction sizeof(array)/sizeof(*array)
goes back to the old C days and returns the number of elements in an array. sizeof
by itself only returns the size in bytes, but if you divide that by the size of a single element, you get the number of elements.
So now that you have a correct "begin" and "end" iterator, you can construct a std::vector<int>
from it. It's just one of the constructors of vector, which takes two iterators.
Your other questions is completely different from the first one.
vector<int> v4(a, *(&a 1));
Now I am not sure this makes entirely sense. Perhaps you have typo here. This would try to call a vector constructor taking an int[]
or int*
as the first parameter and an int as the second parameter.
The signature would be std::vector<int>::vector(int*, int)
, but I do not see such a constructor in the cppreference.
CodePudding user response:
The first piece has several errors:
int arr[5] = [1,2,3,4,5]
std::vector<int> v(arr, arr arr.size()/arr[0])
The correct version would be:
int arr[5] = {1,2,3,4,5};
std::vector<int> v(arr, arr sizeof(arr)/sizeof(arr[0]));
where arr
plus "the number of elements in arr
is a pointer just past the end of arr
.
In the second piece, *(&a 1)
, is a pointer. &a
is a pointer to a
which is an array of 5 integers. Thus adding 1 advances the pointer sizeof(a)
bytes (5 times the size of an integer). Hence, again the result is a pointer just past the end of a
and thus the vector is initialized with the elements of a
.
CodePudding user response:
For starters there is a typo
int arr[5] = [1,2,3,4,5]
std::vector<int> v(arr, arr arr.size()/arr[0])
It seems you mean
int arr[5] = {1,2,3,4,5};
std::vector<int> v(arr, arr sizeof( arr ) / sizeof( arr[0] ));
Though if your compiler supports the C 17 you could write
#include <iterator>
int arr[5] = {1,2,3,4,5};
std::vector<int> v(arr, arr std::size( arr ));
Arrays used in expressions with rare exceptions are converted to pointers to their first elements.
From the C 17 Standard (7.2 Array-to-pointer conversion)
1 An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The temporary materialization conversion (7.4) is applied. The result is a pointer to the first element of the array.
Consider the following demonstration program.
#include <iostream>
int main()
{
int arr[5] = { 1, 2, 3, 4, 5 };
std::cout << "sizeof( arr ) = " << sizeof( arr ) << '\n';
std::cout << "sizeof( arr 0 ) = " << sizeof( arr 0 ) << '\n';
}
Its output might loop like
sizeof( arr ) = 20
sizeof( arr 0 ) = 4
As it is seen from the output the array designator arr
was converted to a pointer in the expression arr 0
.
The same way if an array is used as an argument expression and the corresponding parameter type is not a referenced type when the array is converted to a pointer to its first element.
There is no constructor in the class template std::vector
that has the first parameter having a referenced type to an array.
But there is a template constructor that accepts two first arguments of the same type
template <class InputIterator>
vector(InputIterator first, InputIterator last, const Allocator& = Allocator());
So in this declaration
std::vector<int> v(arr, arr sizeof( arr ) / sizeof( arr[0] ));
there is used two above constructor where two first arguments are converted to pointers of the type int *
.
Pointers have the same operations as a random access iterator. Thus the vector is created using the range of integer values [arr, arr sizeof( arr ) / sizeof( arr[0] )
As for this expression
*(&a 1)
then the expression &a has the type int ( * )[5]
that points to the array a.. The expression &a 1
points to the address pass the last element of the array. The dereferenced expression *(&a 1) has the type int[5] that denotes an array that again used as an expression is converted to pointer of the type int *
.
So you have in fact two expressions of the type int *
in this declaration
vector<int> v4(a, *(&a 1));
However this approach is not correct because you may not dereference a pointer that points after a valid object.