This question already has an answer here:
Why can't simple initialize (with braces) 2D std::array? [duplicate]
(1 answer)
Closed 4 years ago.
In C++, how do I create a multidimensional std::array? I've tried this:
std::array<std::array<int, 3>, 3> arr = {{5, 8, 2}, {8, 3, 1}, {5, 3, 9}};
But it doesn't work. What am I doing wrong and how do I fix this?
You need extra brackets, until c++14 proposal kicks in.
std::array<std::array<int, 3>, 3> arr = {{{5, 8, 2}, {8, 3, 1}, {5, 3, 9}}};
Related
I've created a function to calculate a 3x3 determinant. This is its prototype:
double threeDet(array<array<double, 3>, 3> det);
I've used class array because it seems safer than traditional arrays.
I wanted to test the function, so I called it:
threeDet({ {2, -3, 1}, {2, 0, -1}, {1, 4, 5} });
However, it raises an error of "too many initializers"
This has also happened when I changed the function to:
double threeDet(double det[][3]);
Initializing the array first raises the same error:
array<array<double, 3>, 3> det({ {2, -3, 1}, {2, 0, -1}, {1, 4, 5} });
threeDet(det);
the only thing that worked is to change the function to the second version, and initialize the array separately:
double det[3][3] = { {2, -3, 1}, {2, 0, -1}, {1, 4, 5} };
threeDet(det);
I wonder what am I missing, and if there is a way to initialize that I'm missing.
There's a good rule of thumb to follow until one becomes comfortable with braced initialization lists, and how they work: when a std::array is involved, double-up the braces:
threeDet({{ {{2, -3, 1}}, {{2, 0, -1}}, {{1, 4, 5}} }});
A std::array is an aggregate object containing one class member: the array itself. So:
The outer set of braces construct the std::array.
The inner set of braces construct its class member array.
Now, once you get passed that part, you have a pair of braces that construct each value in the array. Well, each such value is also a std::array, so you need to drop in another pair of braces to initialize its array class member.
When initializing a 2D array we can do
int data[2][2] = {{1, 2}, {3, 4}}; // OK
we can also use
int data[2][2] = {1, 2, 3, 4}; // OK
which makes sense since a 2D array still is a continues chunk of memory.
When using the array class instead of the base type, this works
array<array<int, 2>, 2> data = {1, 2, 3, 4}; // OK
Which again makes sense, since array class doesn't have any extra data and ends up as a continues chunk of memory as well.
But the 2D list does not work:
array<array<int, 2>, 2> data = {{1, 2}, {3, 4}}; // Error
I can also initialize this
vector<array<int, 2>> data = {{1, 2}, {3, 4}}; // OK
But couldn't find anyway to initialize:
array<vector<int>, 2> = ????
My question is:
Is there a fundamental design reason for this (My guess is something related to stuff happening at compile-time vs run-time)? Or is this just an implementation decision for the compiler I am using (GCC)?
std::array doesn't have any user-defined constructor (like std::vector), it just contains a underlying array, when performing aggregate initialization you need one more braces for it.
array<array<int, 2>, 2> data = {{{{1, 2}}, {{3, 4}}}};
// ^ ^ <- for array<array<int, 2>, 2>
// ^ ^ <- for the underlying array
// ^ ^ <- for the 1st array<int, 2>
// ^ ^ <- for its underlying array
// ^ ^ <- for the 2nd array<int, 2>
// ^ ^ <- for its underlying array
We can omit braces as
array<int, 2> data = {1, 2};
array<array<int, 2>, 2> data = {1, 2, 3, 4};
because of brace elision:
The braces around the nested initializer lists may be elided (omitted), in which case as many initializer clauses as necessary are used to initialize every member or element of the corresponding subaggregate, and the subsequent initializer clauses are used to initialize the following members of the object.
That means the above code code be written as
array<array<int, 2>, 2> data = {{{1, 2}, {3, 4}}};
And array<array<int, 2>, 2> data = {{1, 2}, {3, 4}}; fails because it's interpreted as:
array<array<int, 2>, 2> data = {{1, 2}, {3, 4}};
// ^ ^ <- for array<array<int, 2>, 2>
// ^ ^ <- for the underlying array
// ^ ^ <- excess elements; std::array has only one underlying array
I often use initializer lists and for-each loops to iterate through a small number of ad-hoc values, like so:
for (auto x : {1, 2, 6, 24, 120}) {
do_something(x);
}
I recently tried to write something similar, but with structured bindings and packed-together values instead:
for (auto[dx, dy] : {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}) {
try_to_move(dx, dy); // nope! won’t compile
}
Unfortunately, this doesn’t compile. Clang tells me:
error: cannot use type ‘void’ as a range
In fact, even something like auto mylist = {{1, 2}, {3, 4}}; won’t compile.
This leaves me with two questions:
Is there an alternative syntax to accomplish what I want in a terse and readable manner?
Why doesn’t the type of auto mylist get parsed as initializer_list<initializer_list<int>>? Wouldn’t that work fine?
Why doesn’t the type of auto mylist get parsed as initializer_list<initializer_list<int>>? Wouldn’t that work fine?
The reason is that simply no-one proposed it yet.
The handy syntax auto x = {1, 2, 6, 24, 120}; comes from proposal N3912 which was adopted into C++17 (see also N3922).
The deduction process is outlined in [dcl.type.auto.deduct]/4:
If the placeholder is the auto type-specifier, the deduced type T' replacing T is determined using the rules for template argument deduction. Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the initialization is copy-list-initialization, with std::initializer_list<U>. Deduce a value for U using the rules of template argument deduction from a function call, where P is a function template parameter type and the corresponding argument is e. If the deduction fails, the declaration is ill-formed.
Since type deduction in a hypothetical function call f({1, 2}) would fail, so too is the nested braced-init-list deduction auto x = { {1, 2}, {3, 4} }; also impossible.
I guess the same trick could be applied to a deduction from a function call, which would make nested initializer_list deduction possible.
So a follow-up proposal is welcome.
Is there an alternative syntax to accomplish what I want in a terse and readable manner?
You could define a good old multidimensional array:
int lst[][2] = { {1, 2}, {3, 4}, {5, 6}, {7, 8} };
for (auto [x, y] : lst) {
. . .
}
Or as suggested in the comments, give the first pair a type to help the deduction:
for (auto [x, y] : { std::pair{1, 2}, {3, 4}, {5, 6}, {7, 8} }) {
. . .
}
You can use it like this. You just need to initialize your ad-hoc list to a variable first.
vector<pair<int, int> > p = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
for (auto x : p) {
try_to_move(x);
}
You then access the parameter x like this in the function
<return-type> try_to_move(pair<int, int> x){
int dx = x.first;
int dy = x.second;
// TO-DO
}
How do I properly and easily initialize an instance of a class that contains a std::vector of some other class that in itself contains some data.
I understand that it is really hard to explain it in words, so I will instead write a piece of code that does not work, but it captures my intention.
#include <vector>
struct Point
{
float x, y;
};
struct Triangle
{
Point points[3];
};
struct Geometry
{
std::vector<Triangle> triangles;
};
int main()
{
Geometry instance
{
{{0,0}, {6, 0}, {3, 3}},
{{5,2}, {6, 6}, {7, 3}}
};
return 0;
}
This code does not work. Clang returns an error -
excess elements in struct initializer
I can not figure out why it is giving me this error.
I imagine that I can initialize
the std::vector of Triangles,
then the array of Points,
then the two floats within each Point object.
How would I go about properly initializing an instance of Geometry class with some values without writing too much code using the initializer brackets?
If you have alternatives, then I am open to considering them.
You need 2 pairs of additional braces for this to work:
Geometry instance
{{
{{{0,0}, {6, 0}, {3, 3}}},
{{{5,2}, {6, 6}, {7, 3}}}
}};
Here's a demo.
Explanations for all the braces:
Geometry instance
{ // for the Geometry object - instance
{ // for the vector member - triangles
{ // for the individual Triangle objects
{ // for the Point array - points[3]
{0,0}, {6, 0}, {3, 3}}}, // for the individual Points
{{{5,2}, {6, 6}, {7, 3}}}
}};
While I like using brace-init lists, when you have nesting this deep it might be more readable to spell out the types explicitly.
You can mention the triangles's type and provide a set of extra parentheses, then it should work
Geometry instance{
std::vector<Triangle> // explicitly mentioning the type
{
{ { {0,0}, {6, 0}, {3, 3}} },
{ { {0,0}, {6, 0}, {3, 3}} }
}
};
I am new in C++
I have simple quesstion
my array:
reporter[3][2] = { {0, 7}, {1, 12}, {2, 3} };
I want to sort like this:
{ {1, 12}, {0, 7}, {2, 3} };
I want to sort by second sector.
thanks a lot
This can be done is some steps:
Take all 2nd elements to a 1D array of structures. Let it is node temp[];
where node is like this:
struct node{
int value;
int position;
};
Sort them using any technique on basis of temp[i].value.
Then copy the array to a resultant array according to the temp[i].position from the sorted array.