Hello all I am trying to implement the Argmin function. following the example from one of the comments here ArgMin for vector<double> in C++?, I have written the following logic.
rev_tone = get_rev_tone(cam_model_path);
//reading the images
Mat img_rev = imread("C:/Users/20181217/Desktop/images/imgs/output_rev.png");
Mat ideal = imread("C:/Users/20181217/Desktop/images/imgs/output_fwd_v6.png");
int idx = 256;
int no_of_channels = 3;
float rev_tone_s[256][3];
///dynamic to static conversion
for (int i = 0; i < rev_tone.size(); i++) {
for (int j = 0; j < rev_tone[i].size(); j++) {
rev_tone_s[i][j] = rev_tone[i][j];
}
}
int dist = distance(rev_tone.begin(), std::min_element(rev_tone.begin(), rev_tone.end()));//rev_tone is of type vector<vector<float>>
cout << dist << endl;
int dist_s = distance(rev_tone_s.begin(), std::min_element(rev_tone_s.begin(), rev_tone_s.end())); //rev_tone_s is a static 2d float of type float[256][3]
cout << dist_s << endl;
when i am executing the program, this line is working without any problem.
int dist = distance(rev_tone.begin(), std::min_element(rev_tone.begin(), rev_tone.end()));//rev_tone is of type vector<vector<float>>
But the nature of my project doesn't allow any dynamic memory allocations. so I have converted the Vectors into arrays (float_rev_tone_s). When I try to perform the Argmin() on the converted array in the following line
int dist_s = distance(rev_tone_s.begin(), std::min_element(rev_tone_s.begin(), rev_tone_s.end())); //rev_tone_s is a static 2d float of type float[256][3]
it gives me an error saying expression must have a class type. I have looked at this error and it says it was a pointer issue. My question is why is it only a problem for an array but not for vector??
According to the cppreference, .begin() of an array also gives the initial iterator.
Is there a way to work around this for defining Argmin() over an array?
Any suggestions will be highly appreciated.
Thanks in advance
It seems like you're confusing C++'s std::array with C-style arrays (which is what you're using in the code you posted):
float rev_tone_s[256][3];
This is a C-style array, not a std::array. C-style arrays are basically raw pointers to objects of the underlying type and a pointer does not have the begin method. std::array on the other hand is an STL container which has the begin method.
This means you could just change the type of your rev_tone_s variable if you want to have stuff like STL-iterators:
std::array<std::array<float, 256>, 3> rev_tone_s;
My question is why is it only a problem for an array but not for vector?
vector is an object, so it has member functions like size() and begin() and end() for iterators. While array is just a raw type, just like and int or a float and not an object, so you don't have any member functions for it.
According to the cppreference, .begin() of an array also gives the initial iterator
That is correct, but that array is std::array which is a wrapper over raw arrays. It doesn't allocate any memory at runtime so you can and should use that. Advantage of using the std::array over raw array is that
You get member functions like size() and iterators.
It won't decay to a pointer whenever you pass it as an argument to a function.
Here's how you declare a 2D std::array:
std::array<std::array<float, 256>, 3> arr ;
Note that you need to #include <array> for this to work.
The problem is that you are trying to invoke begin() on a pointer
Here is an example of using distance with pointer:
#include <iostream>
#include <algorithm>
const int arr_size = 100;
float myarray[arr_size];
int main()
{
for(int i = 0; i < arr_size; ++i) {
myarray[i] = static_cast<float>(100 - i);
}
std::cout << std::distance(myarray, std::min_element(myarray, myarray + arr_size)) << '\n';
return 0;
}
Related
I'm making a simple Snake game. When making a map, my definition of the map is as follows
int map[25][25] = { 0 };
for (int i = 0; i < 25; i++)//Set the boundary to - 2
{
map[0][i] = -2;
map[24][i] = -2;
}
for (int i = 1; i < 25; i++)//Set the boundary to - 2
{
map[i][0] = -2;
map[i][24] = -2;
}
Then I made a function to simulate the motion of the snake。(The first parameter is the class I created: snake,The second is its moving direction. The key is the third parameter, the map array I put in.)
void snake_move(Snake snake1, int direction, int map[][25])
Then I made a call to the function.(The third parameter is the two-dimensional array pointer I passed in)
snake_move(snake1, direction, map);
Then the following figure appears
I found that it was a two-dimensional array before the function call,which is as follows
Why does this happen and how to solve this problem? I look forward to your reply・v・
You cannot pass built-in arrays like this to functions. snake_move(), even though it appears to have an argument that looks like a 2D array, it actually takes a pointer to a 1D array. This:
void func(int map[][25]);
Is actually equivalent to:
void func(int (*map)[25]);
map is a pointer to an array of 25 int elements. When you call that function:
func(map);
The map array "decays" to a pointer that points to its first element.
This is an unfortunate consequence of C++'s compatibility with C.
To avoid issues like this, use std::array (for fixed-size, static allocation of elements), or std::vector (for dynamically allocated elements.)
To get a 2D array, you need to use an array of arrays or a vector of vectors. For an array, that means:
std::array<std::array<int, 25>, 25>
This means "an array containing 25 arrays of 25 int elements.
It's a good idea to make snake_move take a const reference to avoid an unnecessary copy of the whole array. So:
#include <array>
void snake_move(
Snake snake1, int direction,
const std::array<std::array<int, 25>, 25>& map);
// ...
std::array<std::array<int, 25>, 25> map{};
for (int i = 0; i < 25; i++) {
map[0][i] = -2;
map[24][i] = -2;
}
for (int i = 1; i < 25; i++) {
map[i][0] = -2;
map[i][24] = -2;
}
snake_move(snake1, direction, map);
If snake_move() needs to modify the passed array, then remove the const.
To reduce the need to write the type over and over again, you can use an alias (with the using keyword):
using MapType = std::array<std::array<int, 25>, 25>;
void snake_move(Snake snake1, int direction, const MapType& map);
// ...
MapType map{};
// ...
The {} in the map declaration will initialize all values to zero. You can also use:
MapType map = {};
which does the same.
You can actually keep the dimension without using std::array
void snake_move(Snake snake1, int direction, int (&map)[25][25]);
https://godbolt.org/z/EYz7hzjTj
Also note it's not a 1D array (i.e. map[0] is not -2), the debug window does recognize and shows it's a int[25]*, it probably just have some bug that fail to display it in the correct format.
Why does this happen
Because of type decay. In particular, in many contexts (including when appearing as a parameter to a function), an array decays to a pointer to its first element. For example:
The type int [6] decays to int*
The type int *[6] decays to int**.
The type double [10] decays to double*.
The type int [5][6] decays to int (*)[6].
Thus, in you example, the third parameter int map[][25] is actually a pointer to an array of size 25 with elements of type int, ie int (*)[25].
how to solve this problem?
You can use std::array, as shown below:
void snake_move(Snake snake1, int direction,
//----------------------------vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv------->std::array used
std::array<std::array<int, 25>,25> map)
{
}
std::array<std::array<int, 25>,25> map; //sta::array used
If the function snake_move() doesn't change the passed std::array, and to avoid unnecessary copying, you can take the std::array as a reference to const:
void snake_move(Snake snake1, int direction,
const std::array<std::array<int, 25>,25>& map)
//----------------------------^^^^^-----------------------------------^----->lvalue reference to non-const std::array<std::array<int, 25>,25>
{
}
Is it really possible?
I know how to pass 2-D arrays using double pointer. And as per my understanding, it should for 3-D arrays as well. But I would love to be proven wrong. This question will surely reveal how the arrays are interpreted.
The fact here is that the 3-D array is a contiguous block, not some array of pointers of smaller chunks.
This program gives me error :
#include <iostream>
using namespace std;
void display(int ***arr, int l, int m, int n)
{
for(int i=0; i<l; i++)
for(int j=0; j<m; j++)
for(int k=0; k<n; k++)
cout << *(*(*(arr+i)+j)+k) << endl;
}
int main()
{
int arr[][2][2] = {{{1,2},{3,4}},{{10,20},{30,40}}};
display((int***)arr,2,2,2);
}
OUTPUT
test.cpp:17:19: error: cannot convert 'int (*)[2][2]' to 'int***' for argument '1' to 'void display(int***, int, int, int)'
display(arr,2,2,2);
^
2D Arrays passed to double pointer
I believe I can do something similar to 3D arrays as well, but this is way too bad to read.
#include <iostream>
using namespace std;
void display(int **arr, int m, int n)
{
for(int i=0; i<m; i++)
for(int j=0; j<n; j++)
cout << *(*(arr+i)+j) << " " << arr[i][j] << endl;
}
int main()
{
int arr[][3] = {{1,2,3},{4,5,6}};
int *temp[2];
for(int i=0; i<2; i++)
temp[i] = *(arr+i);
display(temp,2,3);
}
OUTPUT
1 1
2 2
3 3
4 4
5 5
6 6
What you do for 2D arrays is correct because you have built an auxiliary array of pointers and you pass that array of pointer as a int **. Even for 2D arrays, this
void display(int **arr, int m, int n);
...
int arr[][3] = {{1,2,3},{4,5,6}};
display(arr,2,3); // WRONG! use a 2D array as an array of pointers
would be wrong.
And anyway, the C++ standard is unfriendly with multi-dimensional arrays: there is no way to write a strictly conformant program passing a multi-dimensional arrays of unknown dimension. Many compilers accept it as extensions, but it may be non portable on other compilers.
The idiomatic way is to only use 1D arrays as the underlying data structure, and provide methods to process it as a multi-dimensional container by internally doing index computations.
BTW I have tried to build a multi-dimensional contiguous container with arbitrary run-time dimensions, respecting all the constraints of the standard containers, and given up after realizing that the standard did not allow it: it is impossible to build a nice iterator over object that do not hold their own data. Here is my best attempt. The answers and comments explain why it is hopeless.
Although you can pass a 1-D array like this
void pass1Darray(int a[])
{
statements;
}
int main()
{
int a[10];
pass1Darray(a);
}
In fact, compiler would see int a[] as int* a and this is the reason why people wonder is it possible to pass a 2-D array by pointer_to_pointer.
But it doesn't make sense!
If you want to pass a 2-D arraybob[5][10], you can see bob as a array and its element is a array, and pass it like this
void pass2Darray( int (*array) [10] ) // it means you pass a pointer which points to a int[10]
{
statements;
}
int main()
{
int bob[5][10];
pass2Darray(bob);
}
This is about passing a 2-D array.
btw, English is not my native langueue, and I'm a beginner of c++ , too.
If there's something wrong, please let me know, thank you.
When you declare an array locally int arr[][2][2] the compiler instantiate a one dimensional vector and "remember" what the offset is to get the right index.
Also local arrays are stored in the stack which is not good if you need large matrices. Another property of int arr[][2][2] is that why when you try to pass it as argument to a function, int arr[][2][2] is the type. You have to specify all the dimensions.
Pointers work differently. If you instantiate a 2D array you need to allocate an array of pointers to rows, and allocate each row array to hold data individually. In C++ I think it's best to use the standard library, which has a standard implementation of dynamic pointers that take care of all allocations. std::vector
In conclusion:
I would use local arrays when the memory required is small and I don't need to use pointers. Local arrays are good if you want to avoid using the heap.
Using pointersm new/delete or malloc/free is allowed but I think it's better to use the standard library in C++, so I would use std::vector in all other scenarios.
I believe I can do something similar to 3D arrays as well, but this is way too bad to read.
Yes, as n.m. explained in their comment
In your 2D code you have created a brand new array of pointers, populated it, and passed it to your function instead of your original 2D array. In your 3D code you have not attempted anything of the sort.
Since the question is tagged as C++, we can use a different toolbox than C coders have to.
In C++, a parameter can be passed to a function by reference, not only by value, and we can write a template that can be used by the compiler to generate the right function for used type.
#include <iostream>
#include <iomanip>
template <class T, size_t L, size_t M, size_t N>
void display( T const (&arr)[L][M][N], int width )
// ^^^^^^
{
for (size_t i = 0; i < L; ++i) {
for (size_t j = 0; j < M; ++j) {
for (size_t k = 0; k < N; ++k) {
std::cout << std::setw(width) << arr[i][j][k];
}
std::cout << '\n';
}
std::cout << '\n';
}
}
int main()
{
int arr[3][2][4] = {
{ {1,2,3,4},
{5,6,7,8}
},
{ {10,20,30,40},
{50,60,70,80}
},
{ {100,200,300,400},
{500,600,700,800}
}
};
display(arr, 5);
}
Live, HERE.
The next step would be, of course, to write a class which encapsulates the concept of a multidimensional array.
So I made a function that takes arrays as parameters and I've tried calling the function by passing arrays that have not been defined as variables into said function (like {0,0,0,0}). However, I am given an error which says "too many initializer values."
Say we have a function defined as:
int func(int values[]) {
int average = 0;
for(int x = 0; x < values.size(); x++) {
average += values[x];
}
return average / values.size();
}
And we want to call it without defining an array to pass in like this: func({1,6,7,2});
Is there any way to do something like this or would I have to define an array and pass it into the function that way?
You cannot do that using built-in arrays. The fact that Arrays are neither Assignable nor Copy-able. Also They are not classes so they don't have member functions like size() or they take Initializer-list.
You can achieve that through using std::array if the size is constant or using std::vector if the size if dynamic.
#include <array>
int func(const std::array<int, 5>& values) {
int average = 0;
for (size_t x{}, sz{ values.size() }; x != sz ; ++x)
average += values[x];
return average / values.size();
}
int main() {
auto ret{
func({ 1, 6, 7, 2 })
};
std::cout << ret << std::endl;
}
Also don't mix Unsigned with Signed in calculations like in your loop:
for(int x = 0; x < values.size(); x++) // x is int while values.size() is unsigned int.
int func(const std::array<int, 5>& values): pass by reference to avoid the copy especially if the size is big. Also pass by const as long as the function doesn't intend to change the parameter also another benefit of using const reference is you can pass literals instead of an object.
N.B: I recommend to also to use range-based for because it is really relevant in your example as long as you want to iterate over all the elements and not intending to insert nor to delete elements:
int average = 0;
for (const auto& e : values)
average += e;
Another version of func as #M.M pointed out is to use std::accumalate to do the job for you:
int func(const std::array<int, 5>& values) {
return std::accumulate(values.begin(), values.end(), 0) /
values.size();
}
Using a vector, yes:
#include <vector>
using namespace std;
void f( const vector <int> & v ) {
}
int main() {
f( {1,2,3,4} );
}
Arrays don't work like that. When you pass an array to a function, the address of the first element gets passed like a pointer, and inside the function there is no more information about the size of the array. (Before the compiler itself could infer the size because the array was declared in the scope, but a function can be called from any number of places)
If you want to do something like that you would either have to use a container class, such as a vector, or you could pass a second argument into the function stating the size of the array. Another way is to have some sort of end point in your array, such as is the case with c-strings, for example a null value.
(I'm from C background and new in C++ and its STLs)
I'm writing a C++ array of vectors that will be passed (as a reference of an array of vectors) through a function and will be processed in it.
In this case [in C] I would have passed a pointer to my custom data type (call by value under the hood.)
My code that's giving errors in compile time while trying to do so:
#include <cstdio>
#include <vector>
using namespace std;
/*
the problem is I can't get the syntax. vector<type> &var is
a reference to a single dimension array of vectors.
*/
void pass_arrayOf_vect(vector<int> &array, int lmt);
int main() {
int lmt = 10;
vector<int> lst[lmt];
pass_arrayOf_vect(lst, lmt);
return 0;
}
/*
and the traditional ambiguity of whether using "." or "->" for
accessing or modifying indexes and their members.
*/
void pass_arrayOf_vect(vector<int> &lst, int lmt) {
for (int i = 0; i < lmt; i++) {
lst[i].push_back(i*i);
}
for (int i = 0; i < lmt; i++) {
printf("array[%d]: ", i);
for (int j = 0; j < lst[i].size(); j++) {
printf("%d ",lst[i][j]);
}
printf("\n");
}
printf("\n");
return;
}
In the main function the lst variable is an array of vectors. When you pass this to the pass_arrayOf_vect function you pass a pointer to the first element.
I.e. when you do
pass_arrayOf_vect(lst, lmt);
it's actually the same as doing
pass_arrayOf_vect(&lst[0], lmt);
So the function you call needs to accept a pointer to a vector as its first argument (not a reference):
void pass_arrayOf_vect(vector<int> *array, int lmt);
// ^
// Note use of asterisk instead of ampersand
An even better solution would be to use an std::array of vectors instead. Or if you're on an older compiler without support for std::array, or need the amount to be run-time configurable (in which case you can't use plain C-style arrays anyway), use a vector of vectors.
const int ADJ_MATRIX[VERTEX_NUM][VERTEX_NUM]={
{0,1,1,0,0,0,0,0},
{1,0,0,1,1,0,0,0},
{1,0,0,0,0,1,1,0},
{0,1,0,0,0,0,0,1},
{0,1,0,0,0,0,0,1},
{0,0,1,0,0,0,1,0},
{0,0,1,0,0,1,0,0},
{0,0,0,1,1,0,0,0}
};
typedef struct {
int vertex;
int matrix[VERTEX_NUM][VERTEX_NUM];
int vNum;
int eNum;
}Graph;
void buildGraph(Graph *graph){
graph->vNum = VERTEX_NUM;
graph->eNum = EDGE_NUM;
graph->matrix = ADJ_MATRIX;
}
The error occurs in this sentence:
graph->matrix = ADJ_MATRIX;
I am new to c++. please tell me why this problem occur and how to solve it?
I want to assign ADJ_MATRIX to the matrix in struct.
As was said, you can't assign arrays in C++. This is due to the compiler being a meanie, because the compiler can. It just won't let you do it...
... unless you trick it ;)
template <typename T, int N>
struct square_matrix {
T data[N][N];
};
square_matrix<int, 10> a;
square_matrix<int, 10> b;
a = b; // fine, and actually assigns the .data arrays
a.data = b.data; // not allowed, compiler won't let you assign arrays
The catch? Now the code needs some little things:
const square_matrix<int, VERTEX_NUM> ADJ_MATRIX={{
// blah blah
}}; // extra set of braces
typedef struct {
int vertex;
square_matrix<int, VERTEX_NUM> matrix;
int vNum;
int eNum;
}Graph;
void buildGraph(Graph *graph){
graph->vNum = VERTEX_NUM;
graph->eNum = EDGE_NUM;
graph->matrix = ADJ_MATRIX; // no change
}
And to access the cells, now we need to use graph->matrix.data[1][2]. This can be mitigated by overloading operator[] or operator() for square_matrix. However, this is now getting terribly close to the new std::array class, or the Boost equivalent boost::array, so it might be wise to consider those instead.
Unfortunately (or maybe fortunately, who knows...) you can't just assign one array to another in C++.
If you want to copy an array, you will need to either copy each of it's elements into a new array one by one, or use the memcpy() function:
for( int i = 0; i < VERTEX_NUM; i++ )
for( int j = 0; j < VERTEX_NUM; j++ )
graph->matrix[i][j] = ADJ_MATRIX[i][j];
or
memcpy( graph->matrix, ADJ_MATRIX, VERTEX_NUM * VERTEX_NUM * sizeof(int) );
Arrays are not assignable. You can use memcpy:
memcpy(graph->matrix, ADJ_MATRIX, sizeof(graph->matrix));
You cannot assign an array to another array. You will need to copy the elements from the source to the destination index by index, or use memcpy to copy the data. Array assignment like this is not allowed
You are trying to assign your variable address of a constant data,
try using
memcpy(graph->matrix,ADJ_MATRIX,sizeof(ADJ_MATRIX));//using sizeof(graph->matrix) is safer.
You can't use an array in assignments. You may use cycles or memcpy instead
memcpy(graph->matrix, ADJ_MATRIX, VERTEX_NUM * VERTEX_NUM * sizeof(int));
or
for(int i = 0; i < VERTEX_NUM; ++i){
for(int j = 0; j < VERTEX_NUM; ++j){
graph->matrix[i][j] = ADJ_MATRIX[i][j];
}
}
The error is thrown, because int matrix[VERTEX_NUM][VERTEX_NUM] in a structure definition means that each structure will have a 2D array of integers of the predefined size and matrix is going to be pointing to its first element. The thing is that matrix cannot be assigned to an arbitrary address, because it's a const pointer i.e. its value (the address it's pointing to) cannot change.
You have 2 options here: you can either use memcpy or some stl algorithms to copy the ADJ_MATRIX into matrix directly or you can declare matrix as a pointer and do the assignment that is currently produces an error.
The latter can be done in the following way:
typedef struct {
int vertex;
const int (*matrix)[VERTEX_NUM];
int vNum;
int eNum;
}Graph;
Thus you can do graph->matrix = ADJ_MATRIX assignment, but you won't be able to modify the individual items in matrix due to constness. This means, graph->matrix[0][1] = 3; is not allowed, while you can read the elements freely.