Initializer-list for initializing 2D std::array member - c++

How to initialize a nested (2D) std::array via an initializer-list?
template <std::size_t W, std::size_t H>
class Block
{
std::array<std::array<int, W>, H> block;
public:
template <typename ...E>
Block(E&&...e) : block {{std::forward<E>(e)...}} {}
};
The class Block should able to initialize block member as below:
Block<3, 2> b {{ {1, 2, 3}, {4, 5, 6} }};
Note: We have the ability to initialize the std::array directly in C++11:
std::array<std::array<int, 3>, 2> b {{ {1, 2, 3}, {4, 5, 6} }};
I'm using gcc-4.9.0

The rules for braces are very complicated when it comes to nested structures.
The simplest form in your code would be this:
Block<3, 2> b {1, 2, 3, 4, 5, 6};
That basically omits all the inner braces — these omissions are allowed by the language.
The next syntax, which is slightly complex, is this:
Block<3, 2> b {{1, 2, 3, 4, 5, 6}};
It still omits braces, but as far as Block and as its member is concerned it is FULLY braced. It omits braces for the array and its members.
And this one is FULLY braced:
Block<3, 2> b {{{ {{1, 2,3}}, {{4,5,6}} }}};
It braces for all inner structures.
All forms compiles fine.
See my other answer for detailed explanation:
When can outer braces be omitted in an initializer list?

It may be to do with the interpretation of the standard being overly capricious with the number of braces required to initialize std::array. This fully braced version compiles without issues on GCC 4.8.1:
Block<3, 2> b {
{
{
{ {1, 2, 3} }, { {4, 5, 6} }
}
}
};
Strangely, this version compiles too:
Block<3, 2> b {
{{ {1, 2, 3}, {4, 5, 6} } }
};

Related

Use constructor parameters outside constructor in constexpr class

I am trying to implement matrix-like class, using an std::array to actually store the data. All of the data is known at compile-time.
I want to be able to use initializer-lists to initialize the Matrix. Something along the lines of
Matrix m = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
I also want to be able to set the dimensions of my matrix during instantiation.
My current code is similar to the following:
template<int m, int n>
class Matrix {
public:
using initializer_list2d = std::initializer_list< std::initializer_list<float> >;
using array2d = std::array< std::array<float, m>, n >;
consteval Matrix(initializer_list2d initList) {
// static_assert to check initList length [...]
int i = 0;
for (auto &row : initList) {
// static_assert to check row length [...]
std::copy(row.begin(), row.end(), data_[i].begin());
i++;
}
}
// Definitions of operators and methods [...]
private:
array2d data_;
};
In order to use this though, I have to set the dimensions through the template:
Matrix<3, 3> m = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
It seems to me, that since the constructor is consteval (and all of its parameters have to be known at compile-time anyway) it should somehow bet possible to deduce the dimensions through the initializer-list alone.
Using a dynamic data type (e.g. vector) is not possible for my application, since this program has to be able to run without access to the heap.
Is there some way to achieve this?
Thanks for any help in advance!
Before C++17 there is no deduction of template arguments for a class template, so this is not possible.
From C++17, you can write this deduction guide:
template<int m, int n>
Matrix(float const (&)[n][m]) -> Matrix<m, n>;
and add an extra pair of braces when constructing the Matrix:
Matrix m =
{
{
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}
};
Here's a demo.
I found another interesting solution, using variadic templates:
template<int m, class T, class... U>
struct Matrix {
Matrix(const T (&t)[m], const U (& ...u)[m]) {
std::copy(t, t+m, arr[0]);
int i = 0;
(std::copy(u, u+m, arr[++i]), ...);
}
T (arr[m])[sizeof...(U) + 1];
};
It even allows for getting rid of the second brace required in cigien's answer and doesn't need a template deduction guide (Although it still needs C++17 in order for int m to be automatically deduced):
Matrix mat = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
Also, the compiler seems to be able to optimze this quite a bit, as seen here

How to use template to generate a regular argument list and pass it to run time functions?

For example, I want to generate a reverse ordered array, but the codes are really redundant.
The constructor is on run-time but the array size is on compile time. However, these codes are very similar.
#include <array>
#include <cstdint>
using std::array;
array<int32_t, 5> genReverse5() {
array<int32_t, 5> a{5, 4, 3, 2, 1};
return a;
}
array<int32_t, 4> genReverse4() {
array<int32_t, 4> a{ 4, 3, 2, 1};
return a;
}
array<int32_t, 3> genReverse3() {
array<int32_t, 3> a{ 3, 2, 1};
return a;
}
You can try with std::integer_sequence (since C++14).
template <std::size_t... I>
auto genReverse_impl(std::index_sequence<I...>) {
array<int32_t, sizeof...(I)> a{ (sizeof...(I) - I)... }; // decrease progressively
return a;
}
template<std::size_t N, typename Indices = std::make_index_sequence<N>>
auto genReverse()
{
return genReverse_impl(Indices{});
}
then use it as
genReverse<5>(); // returns array initialized as {5, 4, 3, 2, 1}
genReverse<4>(); // returns array initialized as {4, 3, 2, 1}
genReverse<3>(); // returns array initialized as {3, 2, 1}
LIVE

Integer sequences implementation C++

I have been reading up on compile time sequences and I have a hard time understanding how the program below works. Can anyone out there explain the program below in detail? Thanks!
#include<iostream>
template<int...>
struct ints
{
};
template<int N, int...args>
struct rev_seq : rev_seq<N - 1, 2 * args..., 1> {};
template<int...args>
struct rev_seq<0, args...>
{
using type = ints<args...>;
};
template<int N>
using RS = typename rev_seq<N + 1>::type;
template<int... args>
void fU(ints<args...>&& s)
{
for (const auto& i : { args... }) std::cout << i << " ";
std::cout << std::endl;
}
int main()
{
fU(RS<5>());
}
RS<2>() instantiates rev_seq<2, 2>::type
rev_seq<2, 2>::type is the primary template (not the specialized one) for rev_seq:
template<int C, int N, int... Is>
struct rev_seq : rev_seq<C - 1, N, N - C, Is...>{}
This is a recursive declaration, so it derives from a version of itself like so:
rev_seq<2, 2, (empty int... Is pack)>
derives from
rev_seq<2-1, 2, 2 - 2>
which is rev_seq<1, 2, 0>
That 0 on the end is part of the int... Is pack on the base class
This again recurses
rev_seq<1, 2, 0>
derives from
rev_seq<1-1, 2, 2-1, 0>
which is rev_seq<0, 2, (1, 0)>
See how the last parameter argument gets appended to the pack?
rev_seq<0, 2, (1, 0)> matches the following template specialization for rev_seq:
template<int N, int... Is>
struct rev_seq<0, N, Is...>
{
using type = ints<N, Is...>;
};
Note that this struct doesn't derive from anything
At this point, the type type in the class becomes
ints<2, 1, 0>
See how the specialization causes us to append N to the front of the sequence?
Finally, we pass the constructed ints to a function:
template<int... Is>
void fU(ints<Is...>&& s)
{
for (auto i : { Is... }) std::cout << i << " ";
std::cout << std::endl;
}
The loop iterates over the integers
for (auto i : { Is... })
Here { Is...} is a pack expansion, creating an initializer list for us to iterate over. It's fun to note that this is one of the very few places you can just create and use an initializer list, almost all other cases are relegated to matching a std::initializer_list constructor overload for some class (e.g., std::vector)
Assuming you already know the theory behind variadic template, the best approach I may suggest is to "manually unroll" template definitions.
Most of the cases variadic templates exploit a recursive approaches.
Let's try to do this exercise.
The core part is: RS<5>(). That's just an instance of rev_seq<5, 5>::type. Well, let's go deep into rev_seq.
Its declaration:
template<int C, int N, int... Is>
struct rev_seq // ...
So that instance will be "mapped":
template<5, 5, $>
struct rev_seq // ...
where $ is just a placeholder symbol to indicate an empty variadic list.
rev_seq inherits recursively:
template<5, 5, $>
struct rev_seq : rev_seq <4, 5, 0, $> {}
Of course rev_seq <4, 5, 0, $> (i.e., rev_seq<4, 5, {0}>) inherits and so on.
<5, 5, $> ->
<4, 5, {0}> ->
<3, 5, {1, 0}> ->
<2, 5, {2, 1, 0}> ->
<1, 5, {3, 2, 1, 0}> ->
<0, 5, {4, 3, 2, 1, 0}>
When the first template parameter is 0 we stop. Because in that case we have a partial template specialization.
Here you can see the analogy with the "base case" in the recursion strategy.
Therefore, we finally get this inheritance:
struct rev_seq<0, N, Is...>
{
using type = ints<N, Is...>;
};
In your case:
struct rev_seq<0, 5, {4, 3, 2, 1, 0}>
{
using type = ints<5, {4, 3, 2, 1, 0}>;
};
Note that ints is just a variadic list. That is: ints<5, {4, 3, 2, 1, 0}> actually is ints<{5, 4, 3, 2, 1, 0}>.
So, in the end, you are just calling the "print function" with this particular instance of ints:
template<{5, 4, 3, 2, 1, 0}>
void fU(ints<{5, 4, 3, 2, 1, 0}>&& s)
{
for (auto i : { 5, 4, 3, 2, 1, 0 }) std::cout << i << " ";
std::cout << std::endl;
}
Please note that is not a valid C++ syntax, but more just a "graphical representation" aimed to show the recursive process.

Ambiguous constructor call (I assume)

I have a matrix class defined like so:
template <typename T, unsigned int N, unsigned int M>
class TMatrixNxM //Rows x columns
{
public:
TMatrixNxM(T = T(0)); //Default constructor
TMatrixNxM(const std::array<std::array<T, M>, N>&); //Construct from array
TMatrixNxM(std::initializer_list<std::initializer_list<T>>); //Initializer
//...
private:
std::array<std::array<T, M>, N> data; //ROW-MAJOR
};
Now, in the code which uses matrices I have:
Math::Matrix3x3 b({{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
(Note: Matrix3x3 is a typedef for TMatrixNxM< float, 3, 3> and also, it is in a Math namespace)
Until now, it worked, because I didn't always have that array constructor, only initializer list one. But now, however, the compiler doesn't even finish compiling, it crashes! (I get "stopped working" popup and I have to close it, I am using MS VS Express 2013)
If I do it like this:
Math::Matrix3x3 b = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
Then it works fine. This is what I assume:
When I do it like that, then there is no ambiguity since you can only call the initializer_list constructor that way. With the first approach the compiler may be confused, since array is an aggregate type which means the initialization starts with double braces like: {{...}}, but also since I have an initializer list of initializer lists I have to use double braces. But should it really be a problem, since I never actually do double braces, those are single-braced lists inside a larger single-braces list?
What is really happening here and how do I solve this problem?
Thank you for your time!
EDIT
Compiler doesn't crash anymore if I make the constructor take the array by const pointer (since I'm never really going to straight-out plop the array in the constructor call, I have initializer list for that):
TMatrixNxM(const std::array<std::array<T, M>, N>*);
However can someone explain what was an actual problem before, was my assumption right?
Here's a minimal compilable (or, well, not) code that you can use to test:
#include <array>
#include <initializer_list>
template <typename T, unsigned int N, unsigned int M>
class TMatrixNxM //Rows x columns
{
public:
TMatrixNxM(T = T(0));
TMatrixNxM(const std::array<std::array<T, M>, N>&);
TMatrixNxM(std::initializer_list<std::initializer_list<T>>);
private:
std::array<std::array<T, M>, N> data; //ROW-MAJOR
};
template <typename T, unsigned int N, unsigned int M>
TMatrixNxM<T, N, M>::TMatrixNxM(T par_value)
{
std::array<T, M> temp;
temp.fill(par_value);
data.fill(temp);
}
template <typename T, unsigned int N, unsigned int M>
TMatrixNxM<T, N, M>::TMatrixNxM(const std::array<std::array<T, M>, N> &par_values)
{
data = par_values;
}
template <typename T, unsigned int N, unsigned int M>
TMatrixNxM<T, N, M>::TMatrixNxM(std::initializer_list<std::initializer_list<T>> par_values)
{
int i = 0;
for(std::initializer_list<T> row : par_values)
{
int j = 0;
for(T value : row)
{
data[i][j] = value;
++j;
}
++i;
}
}
int main()
{
TMatrixNxM<float, 3, 3> b({{1, 2, 3}, {4, 5, 6}, {7, 8, 9}});
return 0;
}
If you comment out the constructor declaration/definition with arrays then it should compile and execute fine. As stated previously, I am using compiler from MS VS Express 2013.
Well, looks like it just doesn't work on Microsoft's compiler. I tried to compile the same code on my Linux machine that uses GCC and it compiles with no problems. And it is indeed calling the initializer list constructor as it should. If I make an array in a variable and pass it, it also compiles fine, and it calls the array constructor:
std::array<float, 3> a = {{1, 2, 3}};
std::array<std::array<float, 3>, 3> b = {{a, a, a}};
TMatrixNxM<float, 3, 3> mat(b);
It also compiles and calls the array constructor if I initialize the array directly:
TMatrixNxM<float, 3, 3> mat({{ {{1, 2, 3}}, {{4, 5, 6}}, {{7, 8, 9}} }});

How to give a default value to an std::array with Visual C++ 2012?

How to give a default value to an std::array? For example, something like
void f(std::array<int, 3> pt = std::array<int, 3>{0, 1, 2});
After checking with helpful comments. I think it is due to the compiler. How to work it around with VS 2012 without creating a function like std::array<int, 3> MakeArray(...) ?
Try the following
void f(std::array<int, 3> pt = {0, 1, 2});
Or I would write simply
void f(std::array<int, 3> = {0, 1, 2});
In GCC this code is not compiled. It seems it is a bug of the compiler. However you can write for GCC either as
void f( std::array<int, 3> = std::array<int, 3>( { 1, 2, 3 } ) );
or
void f( std::array<int, 3> = { { 1, 2, 3 } } );