I have this pair of declaration/definition on my Eclipse IDE (in a .h and .hpp respectively):
A( T* [] );
and
A<T>::A(T ** p_proc) { ... }
The first is an array of pointers, the other a pointer of pointers.
I am confused in that they are interchangeable here; Eclipse complains if I replace the ** by * [], throwing a syntax error. Eclipse does not raise errors when I do the opposite, though.
My question is twofold; are these two notations fundamentally the same? Are T [][] and T ** the same as well?
Why does Eclipse throw a syntax error when the hpp file has a type of * [], but not in the header?
My question is twofold; are these two notations fundamentally the same?
No, they are not. T*[] has type array of unknown size of pointer to T whereas T** has type pointer to pointer to T. Arrays and pointers are not identical in general.
However, declaring a function parameter to be of array type is exactly the same as declaring it to be of the corresponding pointer type. If a function parameter is specified to have array type then it's "adjusted" to have the corresponding pointer type. In the same way, int* and int[] aren't the same type, but when you write a function that takes an int[] parameter, it's adjusted so that it takes an int* parameter. (Note that this adjustment is suppressed if the parameter is a reference to array.)
Are T [][] and T ** the same as well?
Actually T[][] is not a valid type at all. In a multidimensional array type, only the first bound may be omitted.
Why does Eclipse throw a syntax error when the hpp file has a type of * [], but not in the header?
Probably because you're writing T*[] p_proc. The correct declarator is T* p_proc[].
They're the same for parameters to functions. A function parameter can't have the type "array of T". If you try to declare a function parameter as having a type "array of T", the compiler will (silently) adjust it to "pointer to T".
That's not the case elsewhere though. Just for example, having something like:
//filea.cpp
int x[SIZE];
and:
//fileb.cpp
extern int *x;
...will not work.
If your Eclipse is not understanding the following case, it's a bug:
template<typename T>
struct A{
A(T **p_proc);
};
template<typename T>
A<T>::A(T *p_proc[]) {}
It's perfectly fine.
Check question about arrays decaying to pointers to understand it in more detail.
Related
A function accepts a value of type int[][26], however, I am unable to define a variable of this type.
void A(int abc[][26]);
I have tried the following
int abc[][26] (can't compile)
int (*abc)[26] (segmentation fault)
How do I define such a variable/
Thanks a lot
Basically you don't. int abc[][26] will take any 2d array of the form int[N][26].
When you work with arrays as function parameters the array decays to a pointer. That means the top most dimension is not needed since it decays to a pointer of the type of elements in the array. So, if you have
void foo(int[10])
what you really have is
void foo(int*)
since it doesn't matter how many elements there are. When you have
void foo(int[10][26])
//or
void foo(int[][26])
then you get
void foo(int(*)[26])
Since the array holds arrays we get a pointer to an array since it doesn't matter how many arrays the pointer points to, we just need to know it points to a int[26].
How do I define variable of type int[][26]?
int[][26] is an array of unknown bound. You cannot define variables of such type.
Arrays of unknown bound can are typically used in contexts where the type is adjusted to be something else. For example, in a function argument list, an array is adjusted to be a pointer to an element of such array. The following are equivalent due to type adjustment:
void A(int abc[][26]);
void A(int (*abc)[26]);
// adjusted argument type is int(*)[26]
Another example of such adjustment is definition of a variable, where the bound is deduced from the initialiser. The following are equivalent due to type adjustment:
int arr[][26] = {{}, {}};
int arr[2][26] = {};
// adjusted array type is int[2][26]
A use case for arrays of unknown bound where the type is not adjusted is in templates, where explicitly providing such array as template type argument can be used to signify that a pointer is to an element in an array, rather than a singular object. For example, std::allocator<int>::deallocate will invoke delete while std::allocator<int[]>::deallocate will invoke delete[].
The following code doesn't compile:
template <int N>
void f(char[N]) {}
int main() {
char buf[10];
f(buf);
}
If I change the char[N] to char (&)[N], it works. So what the difference between them?
You have been bitten by backwards compatibility with C. When you declare a function like:
int f(char c[10]);
You declare a function whose argument is of type char *. The compiler decays the argument type for you. The problem is:
int f(char c[5]);
declares the same function. This is the way C worked, and C++ retained it for compatability.
int f(char (&c)[10]);
Declares a function whose argument is of type "reference to array (length 10) of char". C didn't have references, so there is no need to maintain backwards compatibility.
int f(char (&c)[5]);
Declares a different function - with a different argument type.
obviously you are aware that char [N] is an array and char (&)[N] is a reference to a char [N].
c-style arrays are special when passed as arguments by value. The array itself is not passed, but a reference is.
This 'magic' is an historic side-effect of c++'s evolution from C.
to pass an array by value these days we'd use std::array<char, N> which encapsulates the c-style array.
note that char (&)[N] is treated as a literal type and so may be passed to constexpr functions in constexpr context.
I think the order of events is
The compiler does not look for a function taking an char[10] proper because the language doesn't support passing arrays as values.
The compiler looks for a function taking a reference to char[10] (and doesn't find any).
The compiler looks for a function taking a pointer to char, which is the actual argument type after what's called type adjustment, and doesn't find any.
The last point is interesting because the template function f actually does take a pointer to char, as other posters explained: the index in the declaration is superfluous, and in a function declaration f(char p[]) p is not of type array but of type pointer to char. Note that that differs from declarations of that kind elsewhere (not as a function parameter), where p would be an array, albeit incomplete.
The reason the compiler cannot instantiate the function template is not that its argument type is wrong: It would match after argument type adjustment. The reason is simply that it cannot deduce the template parameter N from the argument. After all, for each N there would be a different f: which one should the compiler take?? After the actual argument buf has been "adjusted" to a pointer to its first element, the length information in the argument is lost. (Yes, compilers are stupid.)
The length information was retained when you declared the function as taking a reference to an array.
Another possibility is to simply instantiate the template function explicitly:
f<10>(buf); works.
The syntax std::unique_ptr<T[]> can be used describe the (templated) type of a unique_ptr whose underlying raw pointer is pointing to an array of Ts. I'm wondering what the syntax T[] means generally. Does it get used outside of smart pointers? Is it possible for e.g. vector<T[]> to ever be useful?
It means "array of unknown bound of T". You might see such a type in function signatures:
void f(int arr[]);
In a declaration of an array defined elsewhere:
extern int arr[];
And obviously, as a type parameter to a template like unique_ptr (or, some time in the future, shared_ptr too). It's an incomplete type, so its usefulness can be rather limited.
vector<T[]> is unlikely to be useful. If you don't know how many elements are in the array, then how could you have a container of them?
On a piece of code in a previous question in stackoverflow I saw this, strange to me, declaration with using:
template <std::size_t SIZE>
class A
{
public:
...
using const_buffer_t = const char(&)[SIZE];
...
};
Could someone please address the following questions:
What type it represents?
Where do we need such kind of declarations?
That's a type alias, a new syntax available since c++11.
What you're actually doing is typedefing the type of an array
const_buffer_t
will be an array of const char with length = SIZE
That using declaration is a new syntax introduced in C++11; it introduces a type alias, specifying that const_buffer_t is now an alias for the type const char(&)[SIZE]. In this respect, this use of using is substantially identical to a typedef (although using type aliases are more flexible).
As for the actual type we are talking about (const char(&)[SIZE]), it's a reference to an array of size SIZE; references to array are rarely used, but can have their use:
if in some function you want to enforce receiving a reference to an array of a specific size instead of a generic pointer, you can do that with array references (notice that even if you write int param[5] in a function declaration it's parsed as int *);
the same holds for returing references to array (documenting explicitly that you are returning a reference to an array of a specific size);
more importantly, if you want to allocate dynamically "true" multidimensional arrays (as opposed to either an array of pointers to monodimensional array or a "flat array" with "manual 2d addressing") you have to use them.
See also the array FAQ, where much of this stuff is explained in detail.
Is it type[]? For example, could I have
T<int[]>;
for some template T.
The type of an "array of type T" is T [dimension], which is what you could pass as template parameters. E.g.:
someTemplate<int [10]> t; // array type as template parameter
int a[5]; // array of 5 ints named 'a'
Arrays need to have a dimension which must be greater than 0. This means that e.g. U u[]; is illegal.
There are cases that might seem like exceptions, the first being parameters:
void f(T[]);
This is a special rule for parameters and f() is actually equivalent to the following:
void f(T*);
Then there is direct inialization of arrays:
int a[] = { 1, 2, 3, 4 };
Here the array size is implicitly given through the number of elements in the initializer, thus the type of a is int[4].
There are also incomplete array types without specificied bounds, but you can't directly create instances of these (see Johannes answer for more):
template<class T> struct X { typedef T type; };
X<int[]>::type a = { 1, 2, 3 };
If you are looking for dynamic arrays, prefer standard containers like std::vector<T> instead.
There are two syntaxes to denote array types. The first is the type-id syntax and is used everywhere where the language expects a compile time type, which looks like:
T[constant-expression]
T[]
This specifies an array type that, in the first form, has a number of elements given by an integer constant expression (means it has to be known at compile time). In the second form, it specifies an array type with an unknown number of elements. Similar to class types that you declare without a body, such an array type is said to be incomplete, and you cannot create arrays of that type
// not valid: what size would it have?
int a[];
You can, however, specify that type. For example you may typedef it
typedef int unknown_int_array[];
In the same manner, you may specify it as a template type argument, so the answer to your question is yes you can pass such a type specifier to a template. Notice that i talk about specifiers here, because the form you use here is not the type itself.
The second way is using the new-type-id syntax which allows denoting runtime types by having non-constant bounds
T[expression]
This allows passing variables as element count, and also allows passing a zero. In such a case, a zero element array is created. That syntax is only usable with the new operator for supporting dynamic arrays.
If possible, you might consider instead using dynamic arrays, and passing in a pointer as the templated type. Such as...
T<int*> myVar;
This started as a comment to Georg's answer, but it ran a bit long...
It seems that you may be missing some key abstraction in your mental model of arrays (at least C-style ones). Local arrays are allocated on the stack with a hard-coded size. If you have an array inside a class or struct, the space for the array is part of the object itself (whether on the stack or heap). Global arrays may even be represented directly in the size of the executable.
This means that any time you want to use an array, you must specify its size to the compiler. The only reason you can leave the brackets empty in a parameter list is because functions treat array parameters as pointers. The function would hardly be useful if it could only operate on one size of array.
Templates are no exception. If you want the size of the templated array to vary, you can add an extra template parameter. You still have to specify the size at compile time for any given instance, though.
The syntax for declaring arrays is
<type> <variable>[<size>];
When using a template the declaration is, in example
template <class T>
T var[4];