Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to declare array with auto

Tags:

I have been playing with auto and I noticed that for most cases you can replace a variable definition with auto and then assign the type.

In the following code w and x are equivalent (default initialized int, but lets not get into potential copies). Is there a way to declare z such that it has the same type as y?

int w{}; auto x = int{}; int y[5]; auto z = int[5]; 
like image 334
Graznarak Avatar asked Jun 05 '13 20:06

Graznarak


People also ask

How do you declare an array?

To create an array, define the data type (like int ) and specify the name of the array followed by square brackets []. To insert values to it, use a comma-separated list, inside curly braces: int myNumbers[] = {25, 50, 75, 100}; We have now created a variable that holds an array of four integers.

How do you declare an array in C++?

A typical declaration for an array in C++ is: type name [elements]; where type is a valid type (such as int , float ...), name is a valid identifier and the elements field (which is always enclosed in square brackets [] ), specifies the length of the array in terms of the number of elements.

What is array .types of array with declaration?

An "array declaration" names the array and specifies the type of its elements. It can also define the number of elements in the array. A variable with array type is considered a pointer to the type of the array elements.

Can array be declared with a variable?

Declaring a Variable to Refer to an Array As with declarations for variables of other types, the declaration for an array variable does not create an array and does not allocate any memory to contain array elements. The code must create the array explicitly and assign it to anArray .


1 Answers

TL;DR

template<typename T, int N> using raw_array = T[N];  auto &&z = raw_array<int,5>{}; 

Your example of auto z = int[5]; isn't legal any more than auto z = int; is, simply because a type is not a valid initializer. You can write: auto z = int{}; because int{} is a valid initializer.

Once one realizes this, the next attempt would be:

auto z = int[5]{}; 

Note that your int y[5] does not have any initializer. If it had then you would have jumped straight here.

Unfortunately this does not work either for obscure syntax reasons. Instead you must find a legal way to name the array type in an initializer. For example, a typedef name can be used in an initializer. A handy reusable template type alias eliminates the burdensome requirement of a new typedef for every array type:

template<typename T, int N> using raw_array = T[N];  auto z = raw_array<int,5>{}; 

Aside: You can use template type aliases to fix the weird 'inside-out' syntax of C++, allowing you to name any compound type in an orderly, left-to-right fashion, by using this proposal.


Unfortunately due to the design bug in C and C++ which causes array-to-pointer conversions at the drop of a hat, the deduced type of the variable z is int* rather int[5]. The resulting variable becomes a dangling pointer when the temporary array is destroyed.

C++14 introduces decltype(auto) which uses different type deduction rules, correctly deducing an array type:

decltype(auto) z = raw_array<int,5>{}; 

But now we run into another design bug with arrays; they do not behave as proper objects. You can't assign, copy construct, do pass by value, etc., with arrays. The above code is like saying:

int g[5] = {}; int h[5] = g; 

By all rights this should work, but unfortunately built-in arrays behave bizarrely in C and C++. In our case, the specific problem is that arrays are not allowed to have just any kind of initializer; they are strictly limited to using initializer lists. An array temporary, initialized by an initializer list, is not itself an initializer list.


Answer 1:

At this point Johannes Schaub makes the excellent suggestion that we can use temporary lifetime extension.

auto &&z = raw_array<int,5>{}; 

decltype(auto) isn't needed because the addition of && changes the deduced type, so Johannes Schaub's suggestion works in C++11. This also avoids the limitation on array initializers because we're initializing a reference instead of an array.

If you want the array to deduce its length from an initializer, you can use an incomplete array type:

template<typename T> using unsized_raw_array = T[];  auto &&z = unsized_raw_array<int>{1, 2, 3}; 

Although the above does what you want you may prefer to avoid raw arrays entirely, due to the fact that raw arrays do not behave like proper C++ objects, and the obscurity of their behavior and the techniques used above.

Answer 2:

The std::array template in C++11 does act like a proper object, including assignment, being passable by value, etc., and just generally behaving sanely and consistently where built-in arrays do not.

auto z = std::array<int,5>{}; 

However, with this you miss out on being able to have the array type infer its own length from an initializer. Instead You can write a make_array template function that does the inference. Here's a really simple version I haven't tested and which doesn't do things you might want, such as verify that all the arguments are the same type, or let you explicitly specify the type.

template<typename... T> std::array<typename std::common_type<T...>::type, sizeof...(T)> make_array(T &&...t) {     return {std::forward<T>(t)...}; }  auto z = make_array(1,2,3,4,5); 
like image 126
bames53 Avatar answered Sep 20 '22 08:09

bames53