Followup to Range-for-statement cannot build range expression with array function parameter.
The original error is:
error: cannot build range expression with array function parameter 'arr' since parameter with array type 'int []' is treated as pointer type 'int *'
The failing code:
void print(int (&arr)[int]){
for(int x: arr)
cout<<" "<<x;
cout<<endl;
}
The fixed code:
template<int N>
void print(int (&arr)[N]){
for(int x: arr)
cout<<" "<<x;
cout<<endl;
}
My question is, why do we have to fix it like this? I do not understand what int (&arr)[int] means.
Your question is a little confusing because your first example is not valid code.
However, I think there is still an explanation to provide here. Here's a "correct" version of your first function:
void print(int (&arr)[5]){
for(int x: arr)
cout<<" "<<x;
cout<<endl;
}
void foo() {
int x[5];
int y[3];
print(x);
print(y); // Bam! error!
}
The print() function takes an array of 5 ints as a parameter. Note that this can only be done with statically sized arrays. The compiler must know the size of the array when compiling the function, and that function will only work for that particular array size.
Let's contrast that with your second, valid example.
A single array size, that's kind of annoying. What if I don't want to write a different function for various array sizes? We could always pass a pointer and a size, but that's kind of ugly (not to mention error prone).
Instead, we can give the compiler a function template. That is, not a function, but a recipe on how to create functions on-demand.
template<int N>
void print(int (&arr)[N]){
for(int x: arr)
cout<<" "<<x;
cout<<endl;
}
The constant 5 has been replaced by the placeholder constant N (which will have to be an int, as specified in the template declaration. This way, the compiler will generate a separate print() function for each array size we call it with.
The rationale behind that is that C++ only allows constepr for array dimensions. The template version ensures that N is a constepr (a compile time constant) while the incorrect one let think that you intend to use a run time integer value for the array size - which is unfortunately forbidden in C++
Related
In the following code, std::extent<decltype(columns)>::value calculates the length of the given array. However, when the array is a function argument, the compiler behaves in different way. Could some one help me how to fix it?
output:
local array length: 5
function array length: 0
code:
#include <iostream>
#include <string>
void showcolumns_num(std::string columns[])
{
int columns_num=std::extent<decltype(columns)>::value;
std::cout<<"function array length: "<<columns_num<<std::endl;
}
int main()
{
std::string column_list[]={"col1","col2","col3","col4","col5"};
// local calculation of column number
int columns_num=std::extent<decltype(column_list)>::value;
std::cout<<"local array length: "<<columns_num<<std::endl;
// function calculation of column number
showcolumns_num(column_list);
return 0;
}
You have to pass array by reference to avoid the decay to pointer which so loses size information:
template <std::size_t N>
void showcolumns_num(std::string (&columns)[N])
Live example.
That because of the declaration:
void showcolumns_num(std::string columns[])
is the same as:
void showcolumns_num(std::string * columns)
But declaration:
std::string column_list[]={"col1","col2","col3","col4","col5"};
is the same as:
std::string column_list[5]={"col1","col2","col3","col4","col5"};
So compiler doesn't know about array size inside the function.
Just use the std::vector< std::string >.
The short answer is: Don't use arrays. Instead of string columns[N];, use vector<string> columns; or vector<string> columns(N,"");. In this answer, I'll talk a bit more about arrays, they are "interesting". But arrays are "interesting" in the way that cancer is interesting, somebody has to understand cancer, but we want to get rid of it and most people don't want to be experts.
C arrays are really weird things. They can't be passed by value, but they can be passed by reference, and C++ makes it quite easy. If you are determined - as an intellectual exercise - to pass arrays, then you can use this:
template<size_t N>
void showcolumns_num(std::string (&columns)[N])
Non-array types, like int, or struct Person, or list<vector<string>>, can be passed by value or by reference. But arrays cannot be passed by value.
If you attempt to pass an array by value, the compiler will do a trick where it will instead pass a pointer to the first element of the array. This is called pointer decay.
This means that, without warning, the compiler will rewrite your function declarations
void showcolumns_num(std::string columns[]) { // this is what you write
// changed to
void showcolumns_num(std::string* columns) { // ... but this is what you get
and every call to showcolumns_num will be changed from:
showcolumns_num(column_list); // this is what you write
// changed to
showcolumns_num(&(column_list[0])); // ... but this is what you get
The reason behind this is historical, and is related to an earlier language called B.
Variables are declared as local variables, or as global variables, or as function parameters. For local and global variables, the compiler will generally respect your wishes, but not for function parameters:
void foo(int x[5]) { // silently converted to int *x
int y[10]; // y really will be an array
}
There are tons of similar questions, but still I could not find any answer relevant for the feature of variable length arrays in C99/C11.
How to pass multidimensional variable length array to a function in C99/C11?
For example:
void foo(int n, int arr[][]) // <-- error here, how to fix?
{
}
void bar(int n)
{
int arr[n][n];
foo(n, arr);
}
Compiler (g++-4.7 -std=gnu++11) says:
error: declaration of ‘arr’ as multidimensional array must have bounds for all dimensions except the first
If I change it to int *arr[], compiler still complains:
error: cannot convert ‘int (*)[(((sizetype)(((ssizetype)n) + -1)) + 1)]’ to ‘int**’ for argument ‘2’ to ‘void foo(int, int**)’
Next question, how to pass it by value and how to pass it by reference? Apparently, usually you don't want the entire array to be copied when you pass it to a function.
With constant length arrays it's simple, since, as the "constant" implies, you should know the length when you declare the function:
void foo2(int n, int arr[][10]) // <-- ok
{
}
void bar2()
{
int arr[10][10];
foo2(10, arr);
}
I know, passing arrays to functions like this is not a best practice, and I don't like it at all. It is probably better to do with flat pointers, or objects (like std:vector) or somehow else. But still, I'm a bit curios what is the answer here from a theoretical standpoint.
Passing arrays to functions is a bit funny in C and C++. There are no rvalues of array types, so you're actually passing a pointer.
To address a 2D array (a real one, not array of arrays), you'll need to pass 2 chunks of data:
the pointer to where it starts
how wide one row is
And these are two separate values, be it C or C++ or with VLA or without or whatnot.
Some ways to write that:
Simplest, works everywhere but needs more manual work
void foo(int width, int* arr) {
arr[x + y*width] = 5;
}
VLA, standard C99
void foo(int width, int arr[][width]) {
arr[x][y] = 5;
}
VLA w/ reversed arguments, forward parameter declaration (GNU C extension)
void foo(int width; int arr[][width], int width) {
arr[x][y]=5;
}
C++ w/ VLA (GNU C++ extension, terribly ugly)
void foo(int width, int* ptr) {
typedef int arrtype[][width];
arrtype& arr = *reinterpret_cast<arrtype*>(ptr);
arr[x][y]=5;
}
Big remark:
The [x][y] notation with a 2D array works because the array's type contains the width. No VLA = array types must be fixed at compile-time.
Hence: If you can't use VLA, then...
there's no way to handle it in C,
there's no way to handle it without a proxy class w/ overloaded operator overloading in C++.
If you can use VLA (C99 or GNU C++ extensions), then...
you're in the green in C,
you still need a mess in C++, use classes instead.
For C++, boost::multi_array is a solid choice.
A workaround
For 2D arrays, you can make two separate allocations:
a 1D array of pointers to T (A)
a 2D array of T (B)
Then set the pointers in (A) to point into respective rows of (B).
With this setup, you can just pass (A) around as a simple T** and it will behave well with [x][y] indexing.
This solution is nice for 2D, but needs more and more boilerplate for higher dimensions. It's also slower than the VLA solution because of the extra layer of indirection.
You may also run into a similar solution with a separate allocation for every B's row. In C this looks like a malloc-in-a-loop, and is analogous of C++'s vector-of-vectors. However this takes away the benefit of having the whole array in one block.
There is no clear cut way for doing this but you can use a workaround to treat a 2 dimensional array as a one dimensional array and then reconvert it to a two dimensional array inside the function.
void foo2(int n, int *arr)
{
int *ptr; // use this as a marker to go to next block
int i;
int j;
for(i = 0; i < n; i++)
{
ptr = arr + i*n; // this is the starting for arr[i] ...
for (j = 0; j < n ;j++)
{
printf(" %d ", ptr[j]); // This is same as arr[i][j]
}
}
}
void bar2()
{
int arr[10][10];
foo2(10, (int *)arr);
}
More quetions:
i get an unrecognized 'eld' from mai structure.
header contains:
const int c=10;
struct Array
{
int n;
int els[c];
};
The error i get is:
error: request for member 'els' in 'A', which is of non-class type 'Array [(((unsigned int)(((int)a) + -0x000000001)) + 1)] {aka Array [(((unsigned int)(((int)a) + -0x000000001)) + 1)]}'
Code:
Array arrayInp()
/* Create 2 vectors by the length defined by the user*/
{
int a,b,i;
cout<<"enter length of the first array: ";
cin>>a;
cout<<"enter length of the second array: ";
cin>>b;
Array A[a],B[b];
cout<<"insert first array:";
for (int i=0;i<a;i++)
{
cin>>A.els[i];
}
cout<<"insert second array:";
for (int i=0;i<a;i++)
{
cin>>B.els[i];
}
return A,B;
}
One more error, my return isent right can someone explain me a way how to return the array structures from the functions?
after build errors:
..\scr\main.cpp:32:10: warning: left operand of comma operator has no effect [-Wunused-value]
..\scr\main.cpp:32:10: error: could not convert '(0, ((Array*)(& B)))' from 'Array*' to 'Array'
..\scr\main.cpp:11:10: warning: unused variable 'i' [-Wunused-variable]
..\scr\main.cpp:33:1: warning: control reaches end of non-void function [-Wreturn-type]
This:
Array A[a]
defines an array of Array called A. You need to index A to access the members of Array or (I suspect this was your intention) change declarations to:
Array A, B;
Just to point out that variable length arrays are not standard C++ (I think they are a GCC extension).
The return statement:
return A,B;
is using the comma operator. In this case B will returned and is the cause of warning: left operand of comma operator has no effect.
The unused variable i warning is complaining that the i declared at the beginning of the function int a, b. i; is unused, this is due to redeclaration of i in both for loops: either remove the i from top declaration or don't redeclare in for loops.
Without seeing the full source code, I suspect the warning: control reaches end of non-void function is because there is no return 0; in int main().
You probably want A[i].els, not A.els[i].
I addition to hmjd comment, I think I should point out that this has already been implemented but better by the standard library; it is almost always better to use std library constructs where possible because they are well know to other C++ developers, their functionality is well documented and finally they have a uniform interface which integrates with other standard features (such as standard algorithms).
There are two construct you can use
The fixed size:
std::array<int, 6> array_; //an array of six elements
And the resizable
std::vector<int> vector_(6); //a container of six elements.
http://en.cppreference.com/w/cpp/container/array
http://en.cppreference.com/w/cpp/container/vector
As i want to find array size dynamically in function, i used sizeof operator. But i got some unexpected result.
here is one demo program to show you, what i want to do.
//------------------------------------------------------------------------------------------
#include <iostream>
void getSize(int *S1){
int S_size = sizeof S1/sizeof(int);
std::cout<<"array size(in function):"<<S_size<<std::endl;
}
int main(){
int S[]={1,2,3,2,5,6,25,1,6,21,121,36,1,31,1,31,1,661,6};
getSize(S);
std::cout<<"array size:"<<sizeof S/sizeof(int)<<std::endl;
return 0;
}
//------------------------------------------------------------------------------------------
compilation command : g++ demo1.cc -o demo1 {fedora 12}
output:
array size(in function):2
array size:19
please explain ,why this is happening.
what can be done to solve this problem.
void getSize(int *S1)
When you pass an array to this function, it decays to pointer type, so sizeof operator will return the size of pointer.
However, you define your function as,
template<int N>
void getSize(int (&S1)[N])
{
//N is the size of array
int S_size1 = N;
int S_size2 = sizeof(S1)/sizeof(int); //would be equal to N!!
std::cout<<"array size(in function):"<<S_size1<<std::endl;
std::cout<<"array size(in function):"<<S_size2<<std::endl;
}
int S[]={1,2,3,2,5,6,25,1,6,21,121,36,1,31,1,31,1,661,6};
getSize(S); //same as before
then you can have the size of array, in the function!
See the demonstration yourself here : http://www.ideone.com/iGXNU
Inside getSize(), you're getting size of pointer, which is 8 bytes (since you're probably running 64-bit OS). In main(), you're getting size of array.
If you want to know array size, pass result of sizeof(S) as additional argument to getSize().
More alternatives would be using some container (like std::vector) or turning function into template function, as Nawaz proposed.
S is an int *, a pointer to an integer, which is a memory address, which is on your machine twice the size of an integer.
If you want the size of the array (I.e., the number of elements), you can't get that directly in pure C. But since this is a c++ question, there is a way: use a vector, which has a size() method.
Actually, this isn't quite true: within the function that you declare S (and only if it's explicitly initialized at compile time as you do in your example -- even new int[19] doesn't work), the sizeof operator actually does get the correct answer, which is why c++ allows you to do this:
int S[]={1,2,3,2,5,6,25,1,6,21,121,36,1,31,1,31,1,661,6};
vector<int> v(S, S + sizeof(S) / sizeof(int) );
and then you can use v.size() (see these docs).
The template version by Nawaz elsewhere is another excellent suggestion which forces the compiler into carrying the full information about the construction of the c++ array around (again, note that this is all known at compile time, which is why you can be explicit about the size in the argument).
you are getting the size of the pointer to the array. If you want the size of the array you have to multiply the number of elements by the size of each element.
You will have to pass the size of the array to the function.
Since you are only passing a pointer to the first element in the array, your function has no information on its actual size.
void getSize(int *S1, size_t size)
{
int S_Size = sizeof(*S1) * size;
}
This is redundant though, if you think about it :D
To prevent this type of accidental misuse of sizeof, you can define a function which only works on arrays:
template<class T, int N>
int array_size(T (&)[N]) {
return N;
}
If you use this in your code, you'll see a compiler error when applied to S1, as it is not an array. Plus, it's shorter and a bit more explicit than sizeof array / sizeof array[0] (using the size of the first item means you don't have to repeat the array type).
This also already exists in Boost in a more general form (accepting anything with a size method, such as std::vector).
I found this code , and i m not sure that whether overloading should happen or not.
void print( int (*arr)[6], int size );
void print( int (*arr)[5], int size );
what happens if I pass pointer to an array of 4 elements , to it should come...
any thread will be helpful.
Overloading will happen, and passing the pointer to the array of 4 int's will not match either function. It's clearer if you write them as the equivalent form:
void print( int arr[][6], int size );
void print( int arr[][5], int size );
An N×4 array can be decayed to a pointer to array of 4 int's. And it's well known that 2D arrays having different 2nd dimensions are incompatible.
KennyTM's answer is the correct one. Here's an additional thought, though, based on the fact that your question comes with a C++ tag. In C++, you can use templates with non-type arguments to find out array dimensions:
#include <iostream>
template< std::size_t N >
void print(int (&arr)[N]) {std::cout << N << '\n';}
int main()
{
int arr[6];
print(arr);
return 0;
}
The call would be ambiguous as none of the two overloads would be able to convert to int (*arr)[4]. You need to pass in an element of 5 or 6 elements explicitly.
VS2008 gives:
error C2665: 'print' : none of the 2 overloads could convert all the argument types
(2088): could be 'void print(int (*)[5],int)'
(2093): or 'void print(int (*)[6],int)'
while trying to match the argument list '(int (*)[4], int)'
Hope that helps.