How to Declare Array with Auto

How to declare array with auto

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);

Why can't I create an array of automatic variables?

auto deduces every brace-enclosed initializer list to a std::initializer_list<T>. (See §7.1.6.4.6 including the example).
Unfortunately you cannot initialize an array or even std::array from a std::initializer_list once you have obtained it, but you can use a std::vector.

#include <vector>
#include <array>
#include <initializer_list>

int main()
{
auto x = {1,2,3};
std::array<int, 3> foo1 = x; // won't work for whatever reason
std::vector<int> foo2 = x; // works as expected
return 0;
}

Of course this defeats the whole purpose of what you are trying to do.

I tried writing a work around called make_array but had to realize that this cannot ever work as the size of an initializer_list isn't part of its template arguments and so you only instantiate one make_array template for each T. This sucks.

template<typename T> 
auto make_array(const std::initializer_list<T>& x)
-> std::array<T, x.size()> { } // maaah

Well, apparently you can go for the variadic-template hack mentioned here How do I initialize a member array with an initializer_list?

(C++14) Array of lambdas: error: 'name' declared as array of 'auto'

The C++ language forbids having arrays declared with auto. You have two good options: function pointers and even better - std::function. Something like this:

std::function<bool(const Vector3&, const Vector3&)> selectionFuncs[8] =
{
[&](const Vector3& min, const Vector3& max)
{
return max.x_ == seamValues.x_ || max.y_ == seamValues.y_ || max.z_ == seamValues.z_;
},

[&](const Vector3& min, const Vector3& max)
{
return min.x_ == seamValues.x_;
},

// ...
};

Don't forget to #include <functional>. Then you just use the elements of the array like any other functions.

How to auto create arrays

  1. Read the first line and convert the result to a number that you store to count.
  2. Then add this line: int [] array = new int [count];
  3. Next setup a for loop: for( var i = 0; i < count; ++i ) and in this loop, you read the current line, convert the value to a number and store it to array [i].

Is this explanation simple enough?

For the concrete Java code, you should use your own brain, otherwise it will never change that you don't understand …


If this "100 entries" limit is relevant, you create a list of arrays (List<int[]> arrays) and the sequence of code looks a bit different:

  1. Get the amount of numbers in the file.
  2. Setup a while loop: while( count > 100 ).
  3. In that loop, you create an array for 100 values and store it to the list:
    int [] array = new int [100];
    arrays.add( array );
    Further you read the next 100 lines, convert the values to numbers and store them to array [i]; for that, you use a for loop as above.
  4. The last operation in the while loop is this: count -= 100;.
  5. After the while loop, you add the code from above the separator line, with one addition: after creating the array, you need to add it to the list of arrays before you start the for loop.

Access auto declared array

Which possibilities exist to access a single value explicitly of this array?

It's not an array, but is deduced to a std::initializer_list<double> which can be accessed through an iterator or a range based loop:

#include <iostream>

auto messwerte2 = { 3.5, 7.3, 4.9, 8.3, 4.4, 5.3, 3.8, 7.5 };

int main() {

for(auto x : messwerte2) {
std::cout << x << std::endl;
}
}

Live Demo

To make it an array use an explicit array declaration:

double messwerte2[] = { 3.5, 7.3, 4.9, 8.3, 4.4, 5.3, 3.8, 7.5 };

How to initialize auto-type array of multiple types objects

You need a tuple instead of an array. However then you can't use a for loop but you have to exploit compile-time methods as the fold expression in the code below:

#include <tuple>
#include <iostream>

using namespace std;

struct gray_pixel {
uint8_t gray;
auto intensity() const {
return gray - 127;
}
};

struct rgb_pixel{
float red, green, blue;
auto intensity() const {
return (red - 127 + green -127 + blue - 127) * 255 / 3;
}
};

template<typename T>
auto intensity(const T& pixel){
return pixel.intensity();
}
//find the average intensity of multiple pixels from different types (gray_pixel or rgb_pixel)
template <typename... Ts>
auto average_intensity(Ts&&... args)
{

// you actually don't need this here, but I leave it
// to clarify how to capture the args in a tuple.
// you can use std::apply to perform calculations on the elements
auto tup = std::forward_as_tuple(std::forward<Ts>(args)...);

const float count = sizeof...(Ts);

// a lambda that gets the intensity of a single pixel
auto get_intensity = [](auto&& pix) {
return static_cast<float>(std::forward<decltype(pix)>(pix).intensity());
};
// fold expression to sum all the intensities of the args
auto sum = (get_intensity(args) + ...);
return sum/count;
}

int main()
{

gray_pixel pixel_gray = {255};
rgb_pixel pixel_rgb = {255,127,127};

cout << "sizeof(pixel_gray): " << sizeof(pixel_gray) << endl;
cout << "sizeof(pixel_rgb): " << sizeof(pixel_rgb) << endl;

cout << "intensity(pixel_gray): " << intensity(pixel_gray) << endl;
cout << "intensity(pixel_rgb): " << intensity(pixel_rgb) << endl;

cout << "average_intensity(pixel_gray, pixel_rgb): " << average_intensity(pixel_gray, pixel_rgb) << endl;
return 0;
}

This was the answer to the original question:

You could try it like this:

If all of your Ts are of the same type, you can drop the std::common_type and use the type of the first element instead.
Also I'd suggest to use std::array instead of a C-style array.

#include <utility>
#include <array>

template<typename... Ts>
auto average_intensity(Ts&&... ts) {

using Auto = std::common_type_t <std::decay_t<Ts>...>;

std::array<Auto, sizeof...(Ts)> pixels {std::forward<Ts>(ts)...};

// or if you really want an C-style array
Auto pixels[]{std::forward<Ts>(ts)...};

}

Auto Rvalue Reference (without &&) to an Array in C++14

Clang is correct; see [conv.array]:

An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue
of type “pointer to T”. The result is a pointer to the first element of the array.

Even if the array is a temporary, and therefore a prvalue, it is legal to perform the array-to-pointer conversion on it.

It appears that the restriction on converting array prvalues into pointers was introduced in a patch that resolved gcc bug 53220. It seems that a consensus emerged on the thread that allowing the conversion was dangerous since initializing a pointer variable from the array would not extend the lifetime of the array. However, treating such code as though it is ill-formed is not the correct solution, since, as pointed out later in the thread, it is possible to use this conversion in a safe way, e.g., if the array is being passed to a function that takes a pointer (and therefore will live until the function returns).

You would probably have to file a new bug against gcc in order to get them to fix the issue.

How to initialize all members of an array to the same value?

Unless that value is 0 (in which case you can omit some part of the initializer
and the corresponding elements will be initialized to 0), there's no easy way.

Don't overlook the obvious solution, though:

int myArray[10] = { 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 };

Elements with missing values will be initialized to 0:

int myArray[10] = { 1, 2 }; // initialize to 1,2,0,0,0...

So this will initialize all elements to 0:

int myArray[10] = { 0 }; // all elements 0

In C++, an empty initialization list will also initialize every element to 0.
This is not allowed with C until C23:

int myArray[10] = {}; // all elements 0 in C++ and C23

Remember that objects with static storage duration will initialize to 0 if no
initializer is specified:

static int myArray[10]; // all elements 0

And that "0" doesn't necessarily mean "all-bits-zero", so using the above is
better and more portable than memset(). (Floating point values will be
initialized to +0, pointers to null value, etc.)



Related Topics



Leave a reply



Submit