8

I have the following code:

   enum class MessageDeliveryMethod
   {
      POST_MASTER,
      BUBBLE,

      NUM_ENUMERATORS
   };

   namespace
   {
      using MapType = std::array<
         std::pair<char const*, MessageDeliveryMethod>,
         static_cast<std::size_t>(MessageDeliveryMethod::NUM_ENUMERATORS)
      >;

      MapType g_mapping = {{
         {"POST_MASTER", MessageDeliveryMethod::POST_MASTER},
         {"BUBBLE", MessageDeliveryMethod::BUBBLE},
      }};
   }

This compiles but I don't know why. The g_mapping variable requires an extra level of seemingly redundant curly braces. In other words, I expect the initialization to look like:

MapType g_mapping = {
   {"POST_MASTER", MessageDeliveryMethod::POST_MASTER},
   {"BUBBLE", MessageDeliveryMethod::BUBBLE},
};

(One level of outer braces removed).

My understanding is that prior to C++14, when doing direct initialization the extra level of braces is required. However, copy initialization was not supposed to require this based on this page (look at the example there).

Can anyone explain this?

UPDATE:

This SO question which is presumed to be duplicated by my question does indeed answer some specific and helpful questions (related to my own) however out of context mine was confusing due to the usage of pair (which I thought was causing the issue initially). I never would have found that SO question in the first place, so if anything I think perhaps the way I've worded my question may help people arrive to the solution from different angles.

Community
  • 1
  • 1
void.pointer
  • 24,859
  • 31
  • 132
  • 243
  • Plusone for the title. The brace elision are really a ... disbrace? – Kerrek SB Mar 19 '15 at 16:52
  • I have edited my post to clarify why I do not feel it is a duplicate. The SO moderators/community have gotten very bad lately about prematurely closing questions as duplicates instead of considering everyone else's perspectives. – void.pointer Mar 19 '15 at 17:06
  • Arriving from different angles is the purpose of keeping the duplicate questions on the site. It's not a problem that this leads to existing answers. – Bill Woodger Mar 19 '15 at 17:09

3 Answers3

7

std::array is defined as a structure that contains an array.

Thus the first pair of braces are used to initialize data members of the structure that is the array. The second pair of braces is used to initialize the array within the structure. And the third pairs of braces are used to initialize each object of type std::pair.

To be more precise then according to the C++ Standard (23.3.2.1 Class template array overview)

2 An array is an aggregate (8.5.1) that can be initialized with the syntax

array<T, N> a = { initializer-list }; 

where initializer-list is a comma-separated list of up to N elements whose types are convertible to T.

void.pointer
  • 24,859
  • 31
  • 132
  • 243
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thanks, that makes sense. This seems like a design issue of `std::array` then. I'd suggest it could be solved by simply providing a constructor overload for `std::initializer_list` but that would make it a non-aggregate type as is required. Not sure what the solution would be, but it is a bit awkward to use as it is now. – void.pointer Mar 19 '15 at 16:57
2

std::array is defined to be a nested aggregate - a class containing an array as its only member. Prior to C++14, aggregate initialisation needed two levels of braces: one to surround the class member list (in which there was only one member, the array), and one to surround the array element list. Then you need a third level if, as here, you want to list-initialise each array element.

C++14 allows you to "flatten" the lists when initialising nested aggregates, as described in the page you link to.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • `std::pair` is not an aggregate. –  Mar 19 '15 at 16:59
  • 1
    @remyabel: No it isn't; but it can still be list-initialised. I was describing why you need the outer two levels of braces, not the ones around each `pair` initialiser. – Mike Seymour Mar 19 '15 at 17:00
  • There are plenty of duplicates on std::array requiring two sets of braces, but in this specific scenario, you can't omit any braces. –  Mar 19 '15 at 17:02
  • @remyabel: You could replace the ones around each pair with some other form of initialisation. I'm not quite sure what point you're trying to make; my reading of the question is that it's asking about the outer two pairs. Sorry if you'd have preferred me to find a duplicate rather than answering the question directly. – Mike Seymour Mar 19 '15 at 17:05
  • What I meant by the duplicate comment was this question seemed to be different because it involved a non-aggregate type, so the answer should be different accordingly. –  Mar 19 '15 at 17:09
  • @remyabel: OK. I've added a note that the third level is to list-initialise each array member. – Mike Seymour Mar 19 '15 at 17:15
0

In the standard we have 23.3.2.1 (3)

An array is an aggregate (8.5.1) that can be initialized with the syntax

array<T, N> a = { initializer-list };

This shows that when you initialize the array with an internalizer list you need to wrap it in curly braces.

Community
  • 1
  • 1
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 2
    "where *initializer-list* is a comma-separated list of up to N elements whose types are convertible to T." – T.C. Mar 19 '15 at 17:00