Can std::begin work with array parameters and if so, how? - c++

I have trouble using std::begin() and std::end() (from the iterator library) with c-style array parameters.
void SetOrigin(const double i_point[3]) {
Vector v;
std::copy(
std::begin(i_point),
std::end(i_point),
v.begin());
this->setOrigin(v);
}
This results in the following error with Visual Studio 2010 (and similar for end):
error C2784: '_Ty *std::begin(_Ty (&)[_Size])' : could not deduce template argument for '_Ty (&)[_Size]' from 'const double []'
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xutility(995) : see declaration of 'std::begin'
Changing the parameter to non-const gives same result.
Trying to specify the parameter as
...
std::begin<const double, 3>(i_point),
std::end<const double, 3>(i_point),
...
Gives:
error C2664: '_Ty *std::begin<const double,3>(_Ty (&)[3])' : cannot convert parameter 1 from 'const double []' to 'const double (&)[3]'
Is it just not possible to use std::begin on array parameters because they decay to pointers? Is there a trick to get around this or is it best just to not use the iterator functions on array parameters?

Yes, std::begin and std::end can work with parameters that are C style arrays.
The trick is in passing a parameter that's a C style array. When you specify a 1D array as a normal parameter to a normal function, its type is silently adjusted from "array of T" to "pointer to T". When you call that function, what gets passed isn't the array (as an array), but a pointer to the first element of the array.
It is, however, possible to pass an array by reference to a function template:
template <class T, size_t N>
void function(T (&array)[N]) {
// function body here
}
In this case, where you're passing an actual array (albeit, by reference) rather than a pointer, you can use std::begin and std::end perfectly well. For example:
template <class T, size_t N>
T sum(T (&array)[N]) {
return std::accumulate(std::begin(array), std::end(array), T());
}
Now passing an array is trivial, such as:
int array[] = {1, 2, 3, 4};
auto total = sum(array);
std::begin and std::end themselves are (or at least can be) implemented similarly to sum--the array is passed by reference, so they can look something like this:
template <class T, size_t N>
T *begin(T (&array)[N]) {
return array;
}
template <class T, size_t N>
T *end(T (&array)[N]) {
return array + N;
}
Note that although these were added to the standard more recently, they don't require any particularly tricky use of templates, so the implementation above should work fine with a plain old C++98 compiler (and, if memory serves, even with pre-standard compilers such as VC++ 6).

First off, note that the parameter declaration const double i_point[3] is absolutely equivalent to const double* i_point. That is, the function takes any pointer to double const independent of the number of elements pointed to. As a result, it doesn't know the size and std::begin() and std::end() can't deduce the size (well, std::begin() doesn't really need to deduce the size anyway).
If you really want to use std::begin() and std::end() you need to pass an array with three element or a reference to such a beast. Since you cannot pass arrays by value, your best bet is to pass it by reference:
void SetOrigin(double const (&i_point)[3]) {
// ...
}
This function only accepts arrays with exactly three elements as arguments: You cannot pass a pointer to three doubles or a part of a bigger array. In return, you can now use std::begin() and std::end().

void SetOrigin(const double i_point[3])
is as same as
void SetOrigin(const double i_point[])
or
void SetOrigin(const double *i_point)
So, std::begin and std::end can not accept it. In C++ you can not pass an array but as a pointer or reference. If it's a pointer then it doesn't carry any information of passed array.
Your alternatives are std::vector or std::array.

Have you looked at std::array?
It works better with other STL components

While not directly answering your question (it has already been answered sufficiently by M M. and Dietmar Kühl), you appear to want to initialize some std::vector in this function. That said, why not just have:
std::vector v;
std::copy(std::begin(x), std::end(x), std::back_inserter(v));
// or std::copy(x, x + 3, std::back_inserter(v));
Instead of a function call to your function that is trying to do this?
Alternatively, you could write you function like this:
template<typename RandomIterator>
void SetOrigin(RandomIterator start, RandomIterator end)
{
std::vector<int> v;
std::copy(start, end, std::back_inserter(v));
SetOrigin(v);
}
and then call it using:
double xyz[3];
SetOrigin(std::begin(xyz), std::end(xyz));

Related

Why can I not copy a C-style array to std::array? [duplicate]

This question already has answers here:
Can std::begin work with array parameters and if so, how?
(5 answers)
Closed 4 years ago.
I have this code:
std::array<int,16> copyarray(int input[16])
{
std::array<int, 16> result;
std::copy(std::begin(input), std::end(input), std::begin(result));
return result;
}
When I try to compile this code, I am getting this error:
'std::begin': no matching overloaded function found
and a similar error for std::end.
What is the problem and how I can fix it?
In parameter declaration, int input[16] is same as int* input. And when you pass argument array would decay to pointer, both mean the information about size of the array is lost. And std::begin and std::end can't work with pointers.
You could change it to pass-by-reference, which reserve the array's size.
std::array<int,16> copyarray(int (&input)[16])
Note that you can only pass array with exact size of 16 to the function now.
Everything important said already, you can get the function just a little bit more flexible:
template <typename T, size_t N>
std::array<T, N> copyarray(T const (&input)[N])
{
std::array<T, N> result;
std::copy(std::begin(input), std::end(input), std::begin(result));
return result;
}
(Late) edit: There is a disadvantage with the approach above: You'll need to copy the returned array on assignment, as it doesn't contain any truely movable data (that's the same for raw arrays already). You can avoid this drawback by directly copying into the target array:
template <typename T, size_t N>
void copyarray(std::array<T, N>& target, T const (&source)[N])
{
std::copy(std::begin(source), std::end(source), std::begin(target));
}
This mimicks the assignment target = source; if you like better, you can swap the parameters, of course, to have the output parameter last.
Usage (as is):
int source[7] = { };
std::array<int, sizeof(source)/sizeof(*source)> target;
copyarray(target, source);
As stated already the problem here is that arrays decay to pointers when passed to a function, meaning that the size is not preserved.
If however you knew that there were 16 elements in the array you could do this:
array<int,16> copyarray(const int input[]) {
array<int, 16> result;
copy_n(input, size(result), begin(result));
return result;
}

How does std::end know the end of an array?

std::begin and std::end know the beginning and end of a container or an array.
It so easy to know the end and begin of a vector for example because it is a class that gives this information. But, how does it know the end of an array like the following?
int simple_array[5]{1, 2, 3, 4, 5};
auto beg=std::begin(simple_array);
auto en=std::end(simple_array);
std::begin is not that hard to know where the array start. But how does it know where it ends? Will the constant integer 5 be stored somewhere?
I would appreciate if I got an answer with some low-level information.
But, how does it know the end of an array
It uses a template non-type parameter to deduce the size of the array, which can then be used to produce the end pointer. The C++11 signature from the cppreference section for std::end is as follows:
template< class T, std::size_t N >
T* end( T (&array)[N] );
As hvd notes, since it is passed by reference this prevents decay to a pointer.
The implementation would be something similar to:
template< class T, std::size_t N >
T* end( T (&array)[N] )
{
return array + N ;
}
Is the constant integer 5 will be stored some where?
5 or N is part of the type of the array and so N is available at compile time. For example applying sizeof to an array will give us the total number of bytes in the array.
Many times we see an array passed by value to a function. In that case, the array decays to a pointer to type stored in the array. So now the size information is lost. Passing by reference allows us to avoid this loss of information and extract the size N from the type.
is the constant integer 5 will be stored some where?
Yes, it's part of the type of the array. But no, it's not stored anywhere explicitly. When you have
int i[5] = { };
the type of i is int[5]. Shafik's answer talks about how this length is used to implement end.
If you've C++11, using constexpr would be the simple way to go
template <typename T, size_t N>
inline constexpr size_t
arrLen(const T (&arr) [N]) {
return N;
}
If you've a pre-C++11 compiler where constexpr isn't available, the above function may not be evaluated at compile-time. So in such situations, you may use this:
template <typename T, size_t N>
char (&arrLenFn(const T (&arr) [N]))[N];
#define arrLen(arr) sizeof(arrLenFn(arr))
First we declare a function returning a reference to an array of N chars i.e. sizeof this function would now be the length of the array. Then we've a macro to wrap it, so that it's readable at the caller's end.
Note: Two arrays of the same base type but with different lengths are still two completely different types. int[3] is not the same as int[2]. Array decay, however, would get you an int* in both cases. Read How do I use arrays in C++? if you want to know more.
Because you are passing an array to std::end, and an array has type T [N]. std::end can tell when the array ends by looking at the N in the type.

How to get a reference to an array from a pointer to that array

Simple question. I have a pointer to an array.
vector<int> myVector = { 22, 18, 12, -4, 58, 7, 31, 42 };
int* myPtr = myVector.data();
I also have a function that takes a reference to an array as a parameter.
template<typename T> void sort_quick(T (&arr)[]);
How can I pass my vector's array to this function without having to copy the potentially huge array in data().
sort_quick(*arr); // No matching function call for 'sort_quick'
Also, I need to pass it as an array, it's a pre-requisite, so don't come talking about just passing the vector because I wish I could.
Edit:
template<typename T, int N> void sort_quick(T (&arr)[N]);
This should now be legal syntax?
Edit2:
template<typename T> void sort_quick(T* arr, size_t length);
I believe this would be the best version then, when needing to deal with arrays and not vectors.
C-style array bounds must be known at compile-time. Your original definition of sort_quick was illegal. This is legal:
template<typename T, int N> void sort_quick(T (&arr)[N]);
however it can only be called with an actual array.
In order to support sorting containers whose sizes are not known until compile-time, you will need to make a version that takes two parameters. These could be two pointers, or a start pointer and a length. The version taking array could be made to delegate to the new version.
The idiomatic way would be to use iterators:
template<typename Iterator> void sort_quick(Iterator begin, Iterator end);
and then you can call the function like:
sort_quick(myVector.begin(), myVector.end());
The signature for your sorting function takes an array reference; Something you may not know is that no rvalue expression in C or C++ may yield an array, that is to say, an rvalue may not be of an array type. You may also not know about how arrays can decay in to pointers when passed as parameters to functions, but that is perhaps a little off topic;
Regardless, you may not do something like:
sort_quick(my_vec.data());
If you absolutely must use that function with that signature (i.e. it is an interface over which you have no control), you will need to construct an array using the information that the set of values is [my_vec.data(); my_vec.size()] is valid. As to why you can't just use std::vector::data(), if you look carefully, you will see it's return type is T*, and pointers will not magically reverse-decay in to array references.
If you don't use C++11 you can do it that way:
sort_quick( &arr[0] );

Construct std::set from array

Why doesn't C++ provide us with a constructor which takes an array as an argument? Alternatively, is there anything wrong with defining the following function?
template <class T>
std::set<T> ArrayToSet(T array[]) {
return std::set<T>(array, array + sizeof(array) / sizeof(array[0]));
}
I think the answer might come down to ideas of dynamic memory allocation, but I'd like to hear some feedback on this.
Edit: I am not using C++11.
Alternatively, is there anything wrong with defining the following function?
There is: it doesn’t work.
You cannot pass variable-length C-style arrays to functions. The T array[] syntax in an argument list is a synonym for T* array: a raw pointer is passed, not an argument.
You can, however, pass fixed-sized arrays by reference (i.e. T (&array)[5] works). To make this work for different array lengths you need to use a non-type template argument:
template <class T, std::size_t N>
std::set<T> ArrayToSet(T (&array)[N]) {
return std::set<T>(array, array + N);
}
– But I agree in this with Zac: the function is over-specific (or, the other way round: not generic enough). There is already a universal collection-construction method, via iterators. So the correct way is to use C++11’s std::begin and std::end facility, and if you cannot use C++11, then the correct way is to write them yourself:
template <typename T, std::size_t N>
T* begin(T (&array)[N]) {
return array;
}
template <typename T, std::size_t N>
T* end(T (&array)[N]) {
return array + N;
}
Why doesn't C++ provide us with a constructor which takes an array as
an argument?
Why would it? A std::set is not an array, and it already has a constructor that takes iterators to initialize it, so adding another constructor for an array is unnecessary. std::vector IS an array and even it does not have a constructor that takes an array.
Alternatively, is there anything wrong with defining the following
function?
Yes and no. It is unnecessary as you can just write
MyType myArray[mySize];
std::set<MyType> mySet(myArray, myArray + sizeof(myArray) / sizeof(array[0]);
// or std::set<MyType> mySet(myArray, myArray + mySize);
// or std::set<MyType> mySet(std::begin(myArray), std::end(myArray)); c++11
It isn't really worthy of its own function.
If you really want to write a function to help you out, I'd approach it by porting std::begin and std::end to C++03. Those would at least be more usable than a function specifically to create a std::set.
It would look exactly like what Konrad posted in his answer.
std::set does not need to provide a constructor for C style arrays, because it is possible to construct from them already using iterators - furthermore it is not easy to construct from an array, because you could try to construct from T *, which does not convey the array length, or whether it's an array at all.
To do this, we use a template trick to determine the array size and use the iterator constructor:
template <typename T, size_t N>
std::set<T> ArrayToSet(T (& array)[N]) {
return std::set<T>(&array[0], &array[0]+N);
}
Note that, as stated in the comments, this will not work for T * types. You could overload for that giving an additional parameter arr_length or something. In this case sizeof doesn't work either, it would just give you the size of the pointer, not the array.

How can I make an array a parameter of a function in C++?

I have a function in a program I am writing for my school science fair, and what it needs to do is take in an array as a parameter, encrypt the values of the array, and store the encrypted values in a string. How can I do this?
There are several ways to do this. Here are few examples:
C-style
void f(T *array, size_t size);
In this style, the array decay to the pointer to the first argument which gets passed to the function as first argument. Since this conversion loses the size of the array, you've to pass the size as well, which I do as second argument to the function. Use this as:
T array[N]; //N is some compile-time constant.
f(array, N);
C++-style
template<typename T, size_t size>
void f(T (&array)[size]);
void f(std::vector<T> & array);
In this style, you can pass the array by reference, which retains the size of the array, or you can use std::vector<T> instead. Use this as:
T array[N];
f(array); //calls the first version (the template version)
std::vector<T> v;
//fill v
f(v); //calls the vector version
Added by #Mike: or you can use this, which is even better:
template<typename FwdIterator>
void f(FwdIterator begin, FwdIterator end)
{
for( ; begin != end ; ++begin)
{
//your code
}
}
This is better and more generic, because with it, you can use standard containers (such as std::vector, std::list, etc) as well as normal arrays. For example,
T array[N];
std::vector<T> v;
//fill v
f(array, array+N); //okay
f(v.begin(), v.end()); //also okay.
Cool, isn't it?
void YourFunction(const int myarray[]);
Otherwise if you want accept any type
void YourFunction(const void* myGenericArray);
Hope this helps
void encrypt(Array& array, /*other parameters*/)
would do, whatever array type you use.
If your array is a C-style pointer, you can also just pass it:
void encrypt(int* array, /*other parameters*/)