13

Inspired by this answer, I tried next example :

#include <map>
#include <string>
#include <iostream>

int main()
{
  const std::map< int, std::string > mapping = {
      1, "ONE",
      2, "TWO",
    };

  const auto it = mapping.find( 1 );
  if ( mapping.end() != it )
  {
    std::cout << it->second << std::endl;
  }
  else
  {
    std::cout << "not found!" << std::endl;
  }
}

and the compilation failed with next error message (g++ 4.6.1) :

gh.cpp:11:5: error: could not convert '{1, "ONE", 2, "TWO"}' from '<brace-enclosed initializer list>' to 'const std::map<int, std::basic_string<char> >'

I know how to fix it :

  const std::map< int, std::string > mapping = {
      {1, "ONE"},
      {2, "TWO"},
    };

but why the compilation fails in the top example?

Community
  • 1
  • 1
BЈовић
  • 62,405
  • 41
  • 173
  • 273

3 Answers3

25

Because the map is a non-aggregate, and contains non-aggregate elements (std::pair<key_type, mapped_type>), so it requires an initializer-list full of initializer-lists, one for each pair.

std::pair<int,int> p0{ 1,2 }; // single pair
std::map<int, int> m { { 1,2 } }; // map with one element
std::map<int, int> m { { 1,2 }, { 3,4} }; // map with two elements

Bear in mind that the rules for brace elision apply to aggregates, so they do not apply here.

juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • `std::pair` is an aggregate, therefore it shouldn't need an initialization list on it own (like it is explained in [the answer I linked](http://stackoverflow.com/a/11735045/476681). Or, am I wrong? – BЈовић Jul 31 '12 at 14:49
  • 3
    @BЈовић actually, `std::pair` is not an aggregate. I don't know the rules for when `{}` can be *omitted*, but in this case it wouldn't make sense to allow it. I may be able to say more later on... – juanchopanza Jul 31 '12 at 14:58
  • 1
    @BЈовић looking at §8.5, I think the reason is that brace elision is only permitted for aggregate types. – juanchopanza Jul 31 '12 at 18:35
9

The C++11 standard allows braces to be elided only when the target is an aggregate:

8.5.1 Aggregates [dcl.init.aggr]

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

...

(Paragraph 11)

In a declaration of the form

T x = { a };

braces can be elided in an initializer-list as follows. If the initializer-list begins with a left brace, then the succeeding comma-separated list of initializer-clauses initializes the members of a subaggregate; it is erroneous for there to be more initializer-clauses than members. If, however, the initializer-list for a sub- aggregate does not begin with a left brace, then only enough initializer-clauses from the list are taken to initialize the members of the subaggregate; any remaining initializer-clauses are left to initialize the next member of the aggregate of which the current subaggregate is a member.

Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • 3
    @BЈовић wrong, it is not an aggregate. It has constructors and all. – juanchopanza Jul 31 '12 at 15:06
  • 1
    @BЈовић: I thought the question was about `std::map`, but neither `pair` or `map` are aggregates (they have user-provided constructors). – Michael Burr Jul 31 '12 at 15:16
  • @BЈовић: in the example given in the Standard quote, it's `T` that must be an aggregate. `std::map` certainly is not an aggregate - it has an initializer-list constructor that allows using the `{}` construction in the first place. – JohannesD Jul 31 '12 at 16:34
3

Been a long time since I've done C++, but my guess would be because std::map is expecting a set of individual objects, each object containing a key and a value pair.

Having a single list of individual items doesn't make sense, and it's also difficult to read (to make sure that you have a number of items that is exactly divisible by two).

freefaller
  • 19,368
  • 7
  • 57
  • 87