For example
#include <array>
class Range
{
public:
Range(std::array<float, 2> ends) : m_ends(ends) {}
private:
std::array<float, 2> m_ends;
};
and I can
Range r({1, 2});
Now I have another class
class Box
{
public:
Box(std::array<Range, 3> ranges) : m_ranges(ranges) {}
private:
std::array<Range, 3> m_ranges;
};
And I hope I can do the following
Box b({{1,2}, {3,4}, {5,6}});
But I cannot. How can I change the code to make it possible.
std::array
is a bit strange. It doesn't have a user-defined constructor, so it is a lot like a plain struct. So std::array<float,2>
is much like
struct two_floats {
float array[2];
};
Because of this, if you initialize one, you would logically do it like this:
two_floats x = {{1,2}};
std::array<float,2> y = {{1,2}};
The outer braces are for the struct itself, and the inner braces are for the contents of the struct.
It happens to work to only provide one set of braces:
two_floats x = {1,2};
But this is due to a special rule in C++ that allows braces to be omitted in certain cases. Similar to how you can initialize a two-dimensional array with only one set of braces:
float x[2][2] = {1,2,3,4};
And this is what is happening when you initialize your range like this:
Range r({1, 2});
Which is equivalent to
std::array<float,2> arg = {1,2}; // one set of braces omittted
Range r(arg);
But which would more explicitly be written as:
std::array<float,2> arg = {{1,2}};
Range r(arg);
A similar thing happens when initializing the Box. If we explicitly write out the initialization it would look like this:
std::array<float,2> box_arg1 = {{1,2}};
std::array<float,2> box_arg2 = {{3,4}};
std::array<float,2> box_arg3 = {{5,6}};
std::array<Range,3> box_args = {{box_arg1,box_arg2,box_arg3}};
Box b(box_args);
So if we substitute the initializers, we get:
Box b({{{{1,2}},{{3,4}},{{5,6}}}});
and that works. But it is pretty ugly. This initialization is too complex to allow the extra braces to be omitted here, which is the problem you are running into.
One way to work around this to provide additional constructors which take the individual array elements.
class Range
{
public:
Range(float x,float y) : m_ends{x,y} { }
Range(std::array<float, 2> ends) : m_ends(ends) {}
private:
std::array<float, 2> m_ends;
};
class Box
{
public:
Box(Range x,Range y,Range z) : m_ranges{x,y,z} {}
Box(std::array<Range, 3> ranges) : m_ranges(ranges) {}
private:
std::array<Range, 3> m_ranges;
};
And you can now initialize your Box like you originally wanted:
Box b({{1,2}, {3,4}, {5,6}});
I would just drop the arrays and use the usual fields. You can always add an operator[]
overload if you really need it. Just change the field names to whatever it is you're actually modelling.
class Range
{
public:
Range(float x, float y) : m_x{x}, m_y{y}
{}
private:
float m_x, m_y;
};
class Box
{
public:
Box(Range w, Range h, Range d) : m_w{w}, m_h{h}, m_d{d}
{}
private:
Range m_w, m_h, m_d;
};
Live Demo
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With