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.
Related
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.
I'm learning C++ and I've encountered an interesting behavior.
Let's say I've got a struct Point with int x and int y.
I want to create an array of Points using "tagged structure initialization" syntax. This method works, the result is 3:
Point p[3] { {1, 2}, {3, 4}, {5, 6} };
cout << p[1].x;
But if I want to create these with the new keyword, it does not work. I don't get any compiler errors, nor runtime errors, but the fields remain uninitialized:
Point *p = new Point[3] { {1, 2}, {3, 4}, {5, 6} };
cout << p[1].x; // some random number from memory
Regular constructors work this way, but this doesn't. I understand that this is NOT a constructor, just a simplified syntax to assign values to the fields at once, but I'm curious why the first solution works, and the second one not.
Thank You for helping me!
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}}};