struct S {
double arr[1];
S(double arr[1]) : arr(arr) {}
};
int main(void) {
double arr[1] = {1.2};
S p(arr);
return 0;
}
Hi, this is an extracted problem I encountered in my code.
Do you know why this code won't compile?
main.cpp: In constructor ‘S::S(double*)’:
main.cpp:26:28: error: incompatible types in assignment of ‘double*’ to ‘double [1]’
S(double arr[1]) : arr(arr) {}
I am using g++, compiling and running with
g++ -std=c++17 main.cpp kdtree.h kdtree.cpp && ./a.out
Arrays can't be copied.
int a[3];
int b[3];
a = b; // illegal
Further, when you pass an array to a function, its name decays to a pointer, so S(double arr[1]) is equivalent to S(double* arr). Once you're inside the function, you have to copy the individual elements, so you also need the size of the array:
S(double *x, std::size_t sz) {
std::copy_n(x, sz, arr);
}
You can omit the size if you write the template as a function:
template <std::size_t sz)
S(double (&x)[sz]) {
std::copy_n(x, sz, arr);
}
Or, even better, use std::array, which works the way you expect.
Do you know why this code won't compile?
It won't compile because arrays cannot be copy-initialized (except copy-list-initialized, but you're not doing that).
Arrays have to be copied one element at a time, using a loop. There's also a standard algorithm for copying so you don't need to write that loop yourself: std::copy.
Or, you can use std::array that was introduced in C++11. std::array is copyable.
You can't. What you can do is use the std::array wrapper instead:
struct S {
std::array<double, 1> arr;
S(std::array<double, 1> arr) : arr(arr) {}
};
Although the signature of a constructor is written like that, the array type is adjusted to a pointer, and the array argument decays to a pointer to its first element.
However, the arr member in the struct is still of type array. And a pointer can't be assigned to an array (hence the error).
It's also not possible to assign one array to another (e.g. even if you change it to one of the ways described in the above link), so you need to copy the elements manually with a loop, or using std::copy, or use std::array, as in Ron's answer.
Related
I have this class:
template<typename T>
class array_eraser
{
T* array;
int size_array;
public:
array_eraser(T &a[], int s)
{
size_array = s;
array = new T[s];
array = a;
}
}
I call the class with this line in main:
std::string langs[] = { "C++", "Haskell", "Ada", "Python", "Ada" };
array_eraser<std::string> le(langs, sizeof(langs) / sizeof(langs[0]));
and I got this error: cannot convert argument 1 from 'std::string [5]' to 'T *[]
What did i do wrong?
You are nicely demonstrating why not to use raw arrays.
T &a[] is not a reference to an array of Ts but an array a of references to T. Maybe you wanted T (&a)[] - a reference to an array. But that will not fly either. Because in this case you disable the decay mechanics and you have to specify the size. This can be done using the template trick:
template<std::size_t N>
array_eraser(T (&a)[N]) {}
If you really wanted T& a[], that is a no go, T a[] and T& a[] are never compatible.
If you use array_eraser(T a[], int s), the array is not passed by value, but decays into a pointer. There is no difference between T a[] and T* a. Which brings me to your second mistake. array = a; will not copy arrays but only assign the pointers leaking the newly allocated array. You can copy the array using std::memcpy(array,a,s);.
So, unless you really have to and know what you are doing, use std::array or better std::vector if you need run-time and modifiable length. More typing with std:: stuff is not a good enough reason.
I am using PRQA QA C++ as source code analyzer.
This is the first code I analyzed :
void test1(int * var);
void example1()
{
int var1[10];
test1(var1);
}
QA C++ told me
Array type is used as a pointer type argument in the function call.
So, I tried this following example (as it suggested) :
void test2(int (&var)[10]);
void example2()
{
int var2[10];
test2(var2);
}
This time, it told me :
Array type is used as a reference type argument in the function call.
Is there a better solution to use an array parameter ?
The original warning is fine, the second warning is also true.
This is due to arrays decaying to pointers, so var1, originally an array of integers can be used in an expression requiring a pointer.
If you really want to remove these, there are several options:
std::array<int, 10> var1;
test1(var1.data());
Of better:
void test2(std::array<int, 10>& var);
void example2()
{
std::array<int, 10> var2;
test2(var2);
}
Then the second option fixes the size of the array. If it needs to be variable but fixed at compile time, use a template, otherwise use a std::vector instead of a C-style array.
How does passing a statically allocated array by reference work?
void foo(int (&myArray)[100])
{
}
int main()
{
int a[100];
foo(a);
}
Does (&myArray)[100] have any meaning or its just a syntax to pass any array by reference?
I don't understand separate parenthesis followed by big brackets here. Thanks.
It's a syntax for array references - you need to use (&array) to clarify to the compiler that you want a reference to an array, rather than the (invalid) array of references int & array[100];.
EDIT: Some clarification.
void foo(int * x);
void foo(int x[100]);
void foo(int x[]);
These three are different ways of declaring the same function. They're all treated as taking an int * parameter, you can pass any size array to them.
void foo(int (&x)[100]);
This only accepts arrays of 100 integers. You can safely use sizeof on x
void foo(int & x[100]); // error
This is parsed as an "array of references" - which isn't legal.
It's just the required syntax:
void Func(int (&myArray)[100])
^ Pass array of 100 int by reference the parameters name is myArray;
void Func(int* myArray)
^ Pass an array. Array decays to a pointer. Thus you lose size information.
void Func(int (*myFunc)(double))
^ Pass a function pointer. The function returns an int and takes a double. The parameter name is myFunc.
It is a syntax. In the function arguments int (&myArray)[100] parenthesis that enclose the &myArray are necessary. if you don't use them, you will be passing an array of references and that is because the subscript operator [] has higher precedence over the & operator.
E.g. int &myArray[100] // array of references
So, by using type construction () you tell the compiler that you want a reference to an array of 100 integers.
E.g int (&myArray)[100] // reference of an array of 100 ints
The following creates a generic function, taking an array of any size and of any type by reference:
template<typename T, std::size_t S>
void my_func(T (&arr)[S]) {
// do stuff
}
play with the code.
Arrays are default passed by pointers. You can try modifying an array inside a function call for better understanding.
#include <iostream>
#include <array>
#include <cstring>
using namespace std;
int main ()
{
array<int,5> A ;
memset(A,0,sizeof A);
for(int i=0;i < 5;i++){
cout<<A[i]<<" ";
}
return 0;
}
When I run the program, compilation error occured.
But memset works well when I use int A[5] instead of array<int,5> A. Why memset is not working with container array as either way is used to define fixed size array?
Pay close attention to the declaration of memset:
void * memset ( void * ptr, int value, size_t num );
Notice that the first argument must be a pointer.
Raw arrays are special things in c++. You cannot pass them as a parameter to a function as such, but if a function takes a pointer as a parameter, then an array of appropriate type will decay to a pointer to first element. That is why you can pass a raw array to a function that expects a pointer.
std::array is not a raw array, but a regular class. Class instances do not decay to a pointer. That is why you cannot pass an instance of std::array as a parameter of memset.
As explained in comments already, you shouldn't use memset to initialize std::array anyway.
std::memset should work fine with std::array. If you look at the declaratoin of std::memset you will see it takes a pointer to the array, that is why your code does not work.
void* memset( void* dest, int ch, std::size_t count );
This should work:
memset(A.data(), 0, A.size() * sizeof(A[0]);
Like others have pointed out, in your example it is better to initialize the array
array<int, 5> A = {};
memset(A,0,sizeof A); is incorrect. This is an anti-pattern. The way to set an object to all-bits-zero (for objects that permit this operation) is:
memset(&obj, 0, sizeof obj);
or if we have a pointer to object:
memset(ptr, 0, sizeof *ptr);
Your form happens to "work" for C-style arrays because A decays to a pointer but sizeof A doesn't decay. But for non-arrays you will get a size mismatch (or an outright error). Sticking to one of the two canonical forms avoids size mismatches.
std::array is not required to be implemented as a C-style array, so using memset on it is not appropriate. If you are creating the array you can simply write:
array<int,5> A = {};
If you need to reset the array later then you can use:
A.fill(0);
The following code doesn't compile, I am trying to figure out how to calculate the size of an array that is passed into a function and can't seem to get the syntax correct.
The error I am getting is :
Error 1 error C2784: 'size_t getSize(T (&)[SIZE])' : could not deduce template argument for 'T (&)[SIZE]' from 'const byte []' 16 1 sizeofarray
Here is the source code:
#include <cstdint>
#include <stdio.h>
template<typename T, size_t SIZE>
size_t getSize(T (&)[SIZE]) {
return SIZE;
}
typedef std::uint_fast8_t byte;
void processArray(const byte b[])
{
size_t size = getSize(b); // <- line 16 where error occurs
// do some other stuff
}
int main(const int argc, const char* argv[])
{
byte b[] = {1,2,3,4,5,6};
printf("%u\n", getSize(b));
processArray(b);
return 0;
}
If you want this to work, you need to make processArray be a template as well:
template <size_t size>
void processArray(const byte (&b)[size])
{
// do some other stuff
}
C++ does not allow passing arrays by value. If you have a function like this:
void f(int a[5]);
It may look like you are passing an array by value, but the language has a special rule that says a parameter of this form is just another way of saying:
void f(int *a);
So the size of the array is not part of the type at all. This is behavior inhereted from C. Fortunately, C++ has references, and you can pass a reference to an array, like this:
void f(int (&a)[5]);
This way, the size of your array is preserved.
Now, the only remaining trick is to make the function generic, so it can work on any size array.
template <size_t n> void f(int (&a)[n]);
Now, new versions of the function that take references to arrays of different sizes can be generated automatically for you, and the size can be accessed through the template parameter.
As the argument to a function, const byte b[] is treated just like const byte *b. There is no compile-time information about the size of the array that the function was called with.
To pass a reference to the array, you need to make processArray a template and use the same technique. If you don't pass in a reference, the parameter is a pointer, and pointer types don't have array size information.
template<size_t size>
void processArray(const byte (&b)[size]) {
// ...
}
This is the canonical example of why you should use a function template like getSize rather than sizeof, for determining array size.
With sizeof, you'd have gotten the size of a pointer and been none the wiser.
But, this way, you get a compilation error to point out your mistake.
What was the mistake? It was having a function parameter const T arg[] and thinking that this means arg is an array. It's not. This is unfortunate syntax from C that is precisely equivalent to const T* arg. You get a pointer.
This is also why some people think — incorrectly — that "arrays are pointers". They are not. It's just this specific syntax.