c++dictionarystdmaplist-initializationaggregate-initialization

Aggregate initialization with nested map doesn't work expectedly


It seems I was a bit fuzzy when posing this question since it have been a few days that I'm trying to fix this problem and couldn't reproduce it as an MRE probably the issue is somewhere else in my code. I am sorry for the time and effort of those who tried to answer this.


I want to store information about a graphql query so I need to store the selections for each field, this requires using a nested map.

This is my how I tried to implement this however this won't compile for nested fields.

struct NestedContainer;
struct NestedContainer {
  std::map<std::string, NestedContainer *> selections = std::map<std::string, NestedContainer *> ();
};

int main() {
  auto a = NestedContainer({
    {
      {
        "level1",
        {
          {
            {
              "level2",
              {}
            }
          }
        }
      }
    }
  });
  return 0;
}

clang output

<source>:15:13: error: no matching constructor for initialization of 'std::map<std::string, NestedContainer *>' (aka 'map<basic_string<char>, NestedContainer *>')
            {
            ^
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_map.h:204:7: note: candidate constructor not viable: cannot convert initializer list argument to 'const std::less<std::basic_string<char>>'
      map(const _Compare& __comp,
      ^
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_map.h:217:7: note: candidate constructor not viable: cannot convert initializer list argument to 'const map<basic_string<char>, NestedContainer *>'
      map(const map&) = default;
      ^
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_map.h:225:7: note: candidate constructor not viable: cannot convert initializer list argument to 'map<basic_string<char>, NestedContainer *>'
      map(map&&) = default;

gcc

<source>: In function 'int main()':
<source>:25:5: error: could not convert '{{{"level1", {{{"level2", <brace-enclosed initializer list>()}}}}}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, NestedContainer*>'
   25 |     );
      |     ^
      |     |
      |     <brace-enclosed initializer list>
ASM generation compiler returned: 1
<source>: In function 'int main()':
<source>:25:5: error: could not convert '{{{"level1", {{{"level2", <brace-enclosed initializer list>()}}}}}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, NestedContainer*>'
   25 |     );
      |     ^
      |     |
      |     <brace-enclosed initializer list>
Execution build compiler returned: 1

msvc

example.cpp
<source>(14): error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'NestedContainer'
<source>(14): note: No constructor could take the source type, or constructor overload resolution was ambiguous
cl : Command line warning D9002 : ignoring unknown option '-std=c++20'
Compiler returned: 2

godbolt link

For those who ask why I use a pointer, not using a pointer fails as well.

struct NestedContainer {
  std::map<std::string, NestedContainer> selections = std::map<std::string, NestedContainer *> ();
};

int main() {
  auto a = NestedContainer({
    {
      {
        "level1",
        {
          {
            {
              "level2",
              {}
            }
          }
        }
      }
    }
  });
  return 0;
}

msvc

<source>:8:55: error: no viable conversion from 'map<[...], NestedContainer *>' to 'map<[...], NestedContainer>'
  std::map<std::string, NestedContainer> selections = std::map<std::string, NestedContainer *> ();
                                                      ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_map.h:217:7: note: candidate constructor not viable: no known conversion from 'std::map<std::string, NestedContainer *>' (aka 'map<basic_string<char>, NestedContainer *>') to 'const map<basic_string<char>, NestedContainer> &' for 1st argument
      map(const map&) = default;
      ^
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_map.h:225:7: note: candidate constructor not viable: no known conversion from 'std::map<std::string, NestedContainer *>' (aka 'map<basic_string<char>, NestedContainer *>') to 'map<basic_string<char>, NestedContainer> &&' for 1st argument
      map(map&&) = default;
      ^
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_map.h:238:7: note: candidate constructor not viable: no known conversion from 'std::map<std::string, NestedContainer *>' (aka 'map<basic_string<char>, NestedContainer *>') to 'initializer_list<value_type>' (aka 'initializer_list<pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char>>, NestedContainer>>') for 1st argument
      map(initializer_list<value_type> __l,
      ^
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_map.h:204:7: note: explicit constructor is not a candidate
      map(const _Compare& __comp,
      ^
/opt/compiler-explorer/gcc-12.2.0/lib/gcc/x86_64-linux-gnu/12.2.0/../../../../include/c++/12.2.0/bits/stl_map.h:246:7: note: explicit constructor is not a candidate
      map(const allocator_type& __a)

gcc

<source>:8:60: error: could not convert 'std::map<std::__cxx11::basic_string<char>, NestedContainer*>()' from 'map<[...],NestedContainer*>' to 'map<[...],NestedContainer>'
    8 |   std::map<std::string, NestedContainer> selections = std::map<std::string, NestedContainer *> ();
      |                                                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                                                            |
      |                                                            map<[...],NestedContainer*>
<source>: In function 'int main()':
<source>:26:4: error: could not convert '{{{"level1", {{{"level2", <brace-enclosed initializer list>()}}}}}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, NestedContainer>'
   26 |   });
      |    ^
      |    |
      |    <brace-enclosed initializer list>
ASM generation compiler returned: 1
<source>:8:60: error: could not convert 'std::map<std::__cxx11::basic_string<char>, NestedContainer*>()' from 'map<[...],NestedContainer*>' to 'map<[...],NestedContainer>'
    8 |   std::map<std::string, NestedContainer> selections = std::map<std::string, NestedContainer *> ();
      |                                                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |                                                            |
      |                                                            map<[...],NestedContainer*>
<source>: In function 'int main()':
<source>:26:4: error: could not convert '{{{"level1", {{{"level2", <brace-enclosed initializer list>()}}}}}}' from '<brace-enclosed initializer list>' to 'std::map<std::__cxx11::basic_string<char>, NestedContainer>'
   26 |   });
      |    ^
      |    |
      |    <brace-enclosed initializer list>
Execution build compiler returned: 1

msvc

example.cpp
/opt/compiler-explorer/windows/19.00.24210/include/xlocale(341): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
<source>(8): error C2664: 'std::map<std::string,NestedContainer,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>>::map(const std::map<_Kty,_Ty,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>> &)': cannot convert argument 1 from 'std::map<std::string,NestedContainer *,std::less<_Kty>,std::allocator<std::pair<const _Kty,_Ty>>>' to 'std::initializer_list<std::pair<const _Kty,_Ty>>'
        with
        [
            _Kty=std::string,
            _Ty=NestedContainer
        ]
<source>(8): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
<source>(26): error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'NestedContainer'
<source>(26): note: No constructor could take the source type, or constructor overload resolution was ambiguous
cl : Command line warning D9002 : ignoring unknown option '-std=c++20'
Compiler returned: 2

godbolt link


Solution

  • The values in your map are NestedContainer * (pointers), not NestedContainer. So you need to get pointers in the braced-initializer. Something like:

    auto a = NestedContainer{
        {{"level1", new NestedContainer{
            {{"level2", new NestedContainer}} } }}
    };
    

    Alternately, remove the * from the std::map value type.