Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I initialize an object of std::array<std::array<T, 2>, 2>?

I'm trying to initialize objects of type thing:

template<typename T>
  struct thing : std::array<std::array<T, 2>, 2>
  {
  };

thing<int> t1 {{ {1,2}, {3,4} }};

I get:

 error: no matching function for call to ‘thing<int>::thing(<brace-enclosed initializer list>)’
 thing<int> t1 {{{1,2},{3,4}}};

Ditto with

thing<int> t0{{ 1, 2, 3, 4 }};

and several other things.

like image 969
emsr Avatar asked Jul 25 '14 14:07

emsr


1 Answers

If you're using a C++17 compiler, you're only missing an extra set of braces. The following compiles:

thing<int> t1 { { { {1,2}, {3,4} } } };
//            | | | |- braces for inner array
//            | | |--- braces for outer array
//            | |----- braces for base sub object of thing
//            |------- braces for list initialization of thing

C++17 modified the rules for aggregates to allow base classes, as long as they're public and non-virtual.

From §11.6.1/1 [dcl.init.aggr]

An aggregate is an array or a class with
(1.1) no user-provided, explicit, or inherited constructors ([class.ctor]),
(1.2) no private or protected non-static data members ([class.access]),
(1.3) no virtual functions, and
(1.4) no virtual, private, or protected base classes ([class.mi]).

The base classes are now considered elements of the aggregate, and can themselves be initialized using list-initialization.

The elements of an aggregate are:
(2.1) for an array, the array elements in increasing subscript order, or
(2.2) for a class, the direct base classes in declaration order, followed by the direct non-static data members ([class.mem]) that are not members of an anonymous union, in declaration order.


C++14, and earlier, version of the answer follows:

std::array is an aggregate, and the initialization done using a braced-init-list is aggregate initialization. However, thing is not an aggregate because it has a base class.

From §8.5.1/1 [dcl.init.aggr]

An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).

Thus, aggregate initialization is not going to work. Depending on what you're attempting to do, you either want to provide a constructor for thing that takes an std::array<std::array<T, 2>, 2> argument, and initialize the base subobject

template<typename T>
struct thing : std::array<std::array<T, 2>, 2>
{
    thing(std::array<std::array<T, 2>, 2> arr) 
    : std::array<std::array<T, 2>, 2>(arr) 
    {}
};
thing<int> t{ {{ {{1,2}}, {{3,4}} }} };

Or have thing contain the std::array as a data member. Now thing is still an aggregate.

template<typename T>
struct thing
{
    std::array<std::array<T, 2>, 2> arr;
};
thing<int> t{ {{ {{1,2}}, {{3,4}} }} };

If what you're attempting to do is have thing be an alias for an array<array<T,2>,2>, then you don't need either of the above. Use

template<typename T>
using thing = std::array<std::array<T, 2>, 2>;

thing<int> t{{ {{1,2}}, {{3,4}} }};
like image 76
Praetorian Avatar answered Oct 19 '22 10:10

Praetorian