What is an elegant solution to this error from std::find? - c++

I have a class which contains an array which size is unknown at compile time. The array is initialized in the constructor. Then, I have another function that checks if an element is in the array:
class myClass
{
int tab[];
public:
myClass(int array[], int length)
{
std::copy(array, array + length, tab)
}
void myFunction()
{
int x = 8;
int *ptr = std::find(std::begin(tab), std::end(tab), tdc_x);
if (ptr) /* here goes my code */
}
};
I got the following error:
error: no matching function for call to ‘begin(int [0])’
What's wrong with the above piece of code? I know that I can't use std::find with pointers, but my array is an array, not a decayed pointer.
I followed this example. I also included the algorithm header. What am I doing wrong?
I compile my code in C++11.
Edit: I get it now. But how can I do what I want to do in an elegant way?
If I use a pointer instead of the empty array, I won't be able to use std::find.
if I give my array an arbitrary size, I won't be able to copy a bigger array.
What should I do?

int tab[];
The standard doesn't allow empty arrays, but some compilers do as an extension. That doesn't make it legit tho.
If I use a pointer instead of the empty array, I won't be able to use std::find.
Not true, you can still use std::find (s is the size of your tab array).
int *ptr = std::find(tab, tab + s, tdc_x);
if I give my array an arbitrary size, I won't be able to copy a bigger array. What should I do?
Use a std::vector<int>, then call resize()

Related

Why do we specify arrays size as a parameter when passing to function in C++?

I searched this question, most of them says the same thing. Since we only pass the arrays address in a function, compiler can not know the arrays size by looking at the address, they say. I tried to test this by using this code, and both functions gave the same results. So, how does specifying the arrays size as a function parameter help me in a practical way?. In which conditions does specifying the size help us?.
class ArrayTest
{
public:
void say(int ar[])
{
cout<<ar[1]<<endl;
cout<<ar[7]<<endl;
}
void say(int ar[],int sizeAn)
{
cout<<ar[1]<<endl;
cout<<ar[7]<<endl;
}
};
int main()
{
ArrayTest test;
int anAr[5] = {1,2,3,4,5};
test.say(anAr);
test.say(anAr,5);
return 0;
}
This is about you as a programmer having the chance to boundary check, not whether the compiler can do it.
Just try to print out all the elements in the array, with the size:
void say(int ar[],int sizeAn)
{
for(int i=0; i< sizeAn; ++i)
cout<<ar[i]<<endl;
}
now without the size:
void say(int ar[])
{
for(int i=0; i< /*HOW DO I KNOW NOW?*/; ++i)
cout<<ar[i]<<endl;
}
Passing array size as a function parameter is a bad idea, because if you need an array as an array in function passing its size won't have any effect. The array you passed will be decayed to a pointer. So you need to maintain array as is.
Templates provide a simple and effective way to prevent array decay while passing them as function arguments.
template<std::size_t N>
void foo(int (&your_array)[N])
{
for(int i = 0; i < N; i++)
//process array, N will be your array size.
}
//simply pass array when calling the function. N be taken automatically.
//somewhere else
int main()
{
int arr[10];
foo(arr);
}
hope this helps.
Note that your code is invoking undefined behavior because you're accessing element 7 of an array that is only 5 elements big. Using the size parameter, you could for instance check if the index is past its size and not do that call instead.
In your example, you get the same results becaue you aren't actually using the parameter:
void say(int ar[],int sizeAn)
{
cout<<ar[1]<<endl;
cout<<ar[7]<<endl;
}
sizeAn is unused, so it's not making any difference. But consider for instance the following code:
void say(int ar[],int sizeAn)
{
for (int i = 0; i < sizeAn; i++){
cout<<ar[i]<<endl;
}
}
Here, it's printing all the items in the array, so it needs to know how big the array is. If you used an std::vector, for instance, you wouldn't need to pass the size as you can just call the size function, but you can't do that with C style arrays, so you need to pass that size as a parameter if you want to write a function that behaves differently depending on the size).
Or here's a more practical example of your code where the size parameter is used to avoid the undefined behavior:
void say(int ar[],int sizeAn)
{
cout<<ar[1]<<endl;
if (sizeAn >= 8){
cout<<ar[7]<<endl;
}
}
Now it's the same as your code with the change that it's only printing the element 7 if it actually exists.
As you say, compilers can't tell how big an array is if passed to a function. Your first say function tries to reference past the end of the array (ar[7] is beyond the size of 5). Your second say function means you can length check to make sure you don't make this error.
void say(int ar[], int sizeAn)
{
if(sizeAn>1)
cout<<ar[1];endl;
if(sizeAn>7)
cout<<ar[7];endl;
}
This way, YOU know the length and the function can check it before accessing invalid memory locations.
Why do we specify arrays size as a parameter when passing to function in C++?
Do we?
Well, sometimes. The canonical way to pass a range in C++ is using an iterator-pair though, even if I can see it evolve to using ranges when the Range-TS is finally used everywhere.
Anyway, there are other ways to convey what (sub-)range we want to work with. So, let's take a look:
In-band-signalling, like NUL-terminator for c-strings.
An implicit part of the functions contract, like "it will always be exactly 12 elements".
Passing a view of the part we want. Unfortunately, until the ranges-TS is fully incorporated, standard-library-support for that is severely anemic, being restricted to std::string_view in C++17 and extended with std::span for contiguous ranges (like arrays) in C++20 (look at the guideline-support-library for now).
Using an iterator-pair. The full flexibility of iterators, though calculating the length might be costly, or impossible without consuming the range. This is the preferred way in the standard-library.
Using start-iterator and length. Also quite common, but not to the same degree, and does not allow iterators determining the length as you iterate, not that that is an issue here.
Using a (constant where appropriate) reference to the whole container or range, probably templated for generality. This might be combined with point 3, but need not.
Of those, if you know the element-type, and restrict to contiguous arrays, pointer+length is the most comfortable and flexible to use for now, which does not need different code for different lengths, so that's that.

Why is this a pass-by-pointer function?

I'm learning C++ from a course on Udacity.
Can you explain to me why setGrades() was defined as a pass-by-pointer-to-value function? Why is there an error with passing by value? In the code below, I omitted the definition for printGrades() and setID().
#include<iostream>
using namespace std;
const int SIZE = 5;
template <class T>
class StudentRecord
{
private:
const int size = SIZE;
T grades[SIZE];
int studentId;
public:
StudentRecord(T defaultInput);
void setGrades(T* input);
void setId(int idIn);
void printGrades();
};
template<class T>
StudentRecord<T>::StudentRecord(T defaultInput)
{
for(int i=0; i<SIZE; ++i)
grades[i] = defaultInput;
}
template<class T>
void StudentRecord<T>::setGrades(T* input)
{
for(int i=0; i<SIZE;++i)
{
grades[i] = input[i];
}
}
int main()
{
StudentRecord<int> srInt(-1);
srInt.setId(111111);
int arrayInt[SIZE]={4,3,2,1,4};
srInt.setGrades(arrayInt);
srInt.printGrades();
return 0;
}
The output is supposed to be:
ID# 111111: 4
3
2
1
4
C++ does not allow passing builtin C-style arrays by value. One can pass an array by reference or pass a pointer to the first element of the array. Given such pointer, the entire array can be accessed.
Passing-by-pointer is not a usual term in the literature but people keep coining similar terms time and again, which shows some kind of genuine need. The idea behind the term is as follows: one passes a pointer by value but the goal is to let the function access the pointed-to object as an lvalue (which is normally achieved by passing that object by reference).
That's simply because it is an array you want to give to setGrades because you want to set all SIZE values of the array 'grades'.
As you know perhaps, the name of an array can be used like a pointer to the first value of that array.
For example you could write *arrayInt as a term which is equivalent to arrayInt[0].
So when you pass an pointer to the first element of the array to setGrades, the function can get the other elements of that array with arrayName[i] where i is in between 0 and SIZE.
You want to pass an array to setGrades, you can also define it like this:
void StudentRecord<T>::setGrades(T input[])
However the compiler will convert it to a T* pointer automatically.
Functions will always make its own copy of all parameters you gave during compilation (see: call by value). Right here it's an array, but the compiler can't assign an array to another immediately. The only way to do array assignment is to assign its element one by one (or copy the entire memory chunk of the array), and compiler won't do that. The compiler do know that you can access the entire array if you got a pointer of type T pointed to the first element of the array. It's the alternative way of passing the entire array.
And that is the most common way to pass an array to a function.

QuickSort in C++ array.size() error [duplicate]

This question already has answers here:
How do I find the length of an array?
(30 answers)
Closed 6 years ago.
I'm writing some code in C++ to do the quick sort algorithm to sort an array of integers. I have my complete code here:
void swap(int A[], int x, int y)
{
int tmp;
tmp=A[x];
A[x]=A[y];
A[y]=tmp;
}
int partition(int A[], int start, int stop)
{
int big=start;
int pivot=A[stop];
for(int i=start; i<stop; i++)
{
if(A[i]<=pivot)
{
swap(A, i, big);
big++;
}
}
swap(A, big, stop);
return big;
}
void quick_Sort_Helper(int A[], int start, int stop)
{
//base cases
if(stop<=start)
{return;}
if(start+1==stop)
{
if(A[start]>A[stop])
{swap(A, start, stop);}
return;
}
//recursive cases
int pivot=partition(A,start, stop);
quick_Sort_Helper(A,start, pivot-1);
quick_Sort_Helper(A, pivot, stop);
}
void quick_Sort(int A[])
{
quick_Sort_Helper(A,0, A.size()-1);
}
I get an error when compiling the program on the line where I call A.size(). The error reads as follows:
error: request for member ‘size’ in ‘A’, which is of non-class type ‘int*’
I don't understand because the size() function of an array is supposed to return an integer with the length/ number of elements in the array.
Raw arrays are pretty dumb. They are a blocks of memory without any support methods. Further, when you pass them around they tend to decay to pointers losing what little meta information they did have. If you need to preserve this information consider using std::vector (resizable) or std::array (static size).
If these are not available for whatever reason, consider writing a simple wrapping structure to contain the sizing information with the array and pass the structure around.
Arrays do not have methods. So this expression A.size()-1 is invalid. You have to pass explicitly to the function the number of elements in the array. Thus the function declaration will look like
void quick_Sort( int A[], size_t n );
where n is the number of elements in the array A.
Array in C++, unlike Java, is not a class. If you want to use Arrays as a class, you could use array class.
array<int,5> A;
Reference to Array class C++
You can't directly pass array into a function in c++, because arrays are second class citizens and they decay into a pointer. If you still want to pass an array, there is no option but to pass the size as a parameter.
If you declared an array in the same scope as asking for it's size, you can do this,
int l = sizeof(A)/sizeof(A[0]);
In C++, an array does not come with any functionality whatsoever; it is quite literally a block of contiguous memory just large enough to hold whatever data it is declared to hold (in your case it is sizeof(int) * numberOfItems).
I suggest moving to use std::vector instead (in your case this would be std::vector< int >), which under the hood does manage an array, but provides the niceties of size() function, iterators, random access, automatic resizing, etc.
Alternatively, if you are going to use arrays directly like this, you need to pass along its length to any function that is going to operate on the array. Otherwise, the function has absolutely no way of knowing the size of the array.

error : no match for 'operator[]'

I am solving a problem in which I m trying to pass address a 2-D array of a structure and trying to manipulate it but, whenever I use [] operator to access the array elements , I get a compile time error:
no match for 'operator[]'
in my codeblocks IDE.
#include <iostream>
using namespace std;
typedef struct mat
{
int data;
int flag;
} cell;
int mat(cell *);
int main()
{
int m,n;
cin>>m>>n;
cell game[n][m];
cout<<"The length of matrix is "<<mat(&game[0][0]);
}
int mat(cell *arr)
{
return (sizeof(arr[0])/sizeof(arr[0][0]));
}
cell game[n][m];
This is not legal C++. You are using a compiler-specific extension. At this point I advise you against using any compiler-specific extensions. Use standard C++ only. In standard C++ there are no Variable Length Arrays. Don't use arrays in your C++ programs. To get proper variable length array functionality. You should use std::vector instead, like this:
std::vector<std::vector<cell>> game;
Further,
&game[0][0]
is not an address of a 2D array. This is an address of the first element in the array. It contains no information about the number of elements. It is lost forever. You cannot pass it to some function and expect the size of the array to be recovered. To get proper array functionality with a built-in size function, use std::vector.
Last but not least,
(sizeof(arr[0])/sizeof(arr[0][0]));
arr is a cell*. arr[0] is a cell. arr[0][0] is invalid because a cell is neither an array not a pointer, nor it has a custom [] operator defined. In any case you cannot use sizeof to recover the number of elements in the array from a pointer to its first element. To get proper array functionality with a built-in size function, use std::vector.
Two things:
Your function takes a pointer to cell but since you're treating it like a 2D array, you should probably change your signature to either accept a cell ** arr or a cell arr[m][] where m is the (fixed) size of the array and must be specified. Note that these data structures have fundamentally different representations in memory - they just have the same [][] syntax for accessing elements.
You can't use the sizeof function to determine the length of an array if you pass it as a pointer to an elem. You will need to pass the dimensions along with your array, like this:
int mat(cell **arr, int m, int n);
The definition being given basically says that your class doesn't define the operator [], meaning you can't use the syntax you are trying to use.

C++ and recursion with arrays

I've programmed with other languages, but now that I am learning C++, I've found a problem. I am trying to solve a problem with recursion, with a method that takes an array as an argument. I thought about using a public array, maybe, but I can't use the array either way.
From what I've read, it seems to me that it has something to do with the memory. (I thought that, even though it consumes a lot of memory, creating it again with each call would work.)
Here's some code:
static void FindSolution(int row, int column, bool answer[][8][8]) {
for(int i = 0; i < 8; i++)
//Some processing…
bool temp = true;
FindSolution(0, column + 1, answer[row][column] = temp);
}
}
How do I get to actually use the array? Somehow.
The error:
error: array type 'bool [8]' is not assignable
FindSolution(0, column + 1, answer[row][column] = temp);
You have an extra [] on your array. You've declared it as a 3D array, but then you try to assign to it like it is a 2D array. The compiler gets upset because you try to assign a bool value to an array, which is exactly what you are doing:
answer[row][column] = temp;
temp has type bool, but answer[row][column] has type bool[8].
Instead declare the argument without the extra []:
static void FindSolution(int row, int column, bool answer[8][8]) {
You keep incrementing 'column', but you never check it to make sure it doesn't reach 8. When it does reach 8, you're off the end of the array, and you get an error.
There are a few immediate problems with this.
First Problem: Function signature is incorrect
You've declared the third parameter as a 3-dimensional array, but you only want to deal with two dimensions it seems. There are a couple of ways you can redeclare this function to accept a 2D array, for all the options see the accepted answer here. Personally, in this situation I'd go with a template option unless there is a specific reason not to. Something like the following:
template<size_t _rows, size_t _columns>
static void FindSolution(int row, int column, bool (&answer)[_rows][_columns]) {
// todo: Some processing...
}
This allows you to accurately know the size of the array at compile time, of course this won't work so well with dynamically allocated arrays but seeing as you seemed to know the dimensions of the array already at compile time, I figured this wasn't an issue. If it is, check the other ways of passing a 2D array to a function in the link I attached.
Second issue: Recursive call
The second issue is how you're doing your recursive call.
FindSolution(0, column + 1, answer[row][column] = temp);
The result of the assignation of temp to the specific location in the answer array is not the answer array, but rather the value of temp. Effectively the following statement:
answer[row][column] = temp
Is trying to pass a single bool value as a 2-dimensional array, which won't work. In order to correctly call the method again you'll need to do your assignation of temp to the answer array, then call the function again.
answer[row][column] = temp;
FindSolution<_rows,_columns>(0, column + 1, answer);
Should work fine. (Note the explicit template arguments here <_rows,_columns>, this is only needed if you're using the function signature I posted above which made use of templates)