Search code examples
c++variadic-templatesoverload-resolutionemplace

std::map::emplace fails to resolve, but insert of rvalue works -- why?


Consider the following attempt to emplace an empty vector with a numeric key in a map:

#include <map>
#include <vector>

int main () {
    std::map<size_t, std::vector<size_t>> m;
    m.emplace(42, {});                          // FAILS
    m.insert({42, {}});                         // WORKS
}

The call to emplace fails to resolve:

error: no matching function for call to ‘std::map<long unsigned int, std::vector<long unsigned int> >::emplace(int, <brace-enclosed initializer list>)’
     m.emplace(42, {});
                     ^
In file included from /usr/include/c++/8/map:61,
                 from map_emplace.cpp:1:
/usr/include/c++/8/bits/stl_map.h:574:2: note: candidate: ‘std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::emplace(_Args&& ...) [with _Args = {}; _Key = long unsigned int; _Tp = std::vector<long unsigned int>; _Compare = std::less<long unsigned int>; _Alloc = std::allocator<std::pair<const long unsigned int, std::vector<long unsigned int> > >; typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<std::pair<const _Key, _Tp> >::other>::iterator = std::_Rb_tree_iterator<std::pair<const long unsigned int, std::vector<long unsigned int> > >]’
  emplace(_Args&&... __args)
  ^~~~~~~
/usr/include/c++/8/bits/stl_map.h:574:2: note:   candidate expects 0 arguments, 2 provided

Attempting to do the same thing with a vector of vectors works as expected (EDIT: not when using emplace_back, cf. Bo Persson's answer):

    std::vector<std::vector<size_t>> v;
    v.emplace({});           // WORKS -- but does the wrong thing!
    v.emplace_back({});      // FAILS
    v.push_back({{}});       // WORKS

My rough understanding of the logic behind emplace is that the calls to emplace and insert should give the same result, the difference being that emplace requires neither move nor copy. For these types, there is not much harm in using the insert version, since the contents of the vector would be moved (and in this specific case, the vector is empty, anyway). In general though, why does this fail for std::map::emplace? Using GCC 8.1.


Solution

  • signature of map::emplace is

    template< class... Args >
    std::pair<iterator,bool> emplace( Args&&... args );
    

    and {} has no type and cannot be deduced.

    You might use, for emplace:

    m.emplace(42, std::vector<std::size_t>{});
    

    For insert, (one of its) signature is:

    std::pair<iterator,bool> insert( const value_type& value );
    

    so {42, {}} is used to construct std::pair<const int, std::vector<size_t>>.