std::map::emplace не удается разрешить, но вставка rvalue работает - почему?

Рассмотрим следующую попытку вставить пустой vector с числовым ключом в map:

#include <map>
#include <vector>

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

Призыв к emplace не удается решить:

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

Попытка сделать то же самое с вектором векторов работает как ожидалось (РЕДАКТИРОВАТЬ: не при использовании emplace_backср. Ответ Бо Перссона):

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

Мое грубое понимание логики позади emplace что звонки emplace а также insert должен давать тот же результат, с той разницей, что emplace не требует ни перемещения, ни копирования. Для этих типов нет большого вреда в использовании insert версия, так как содержимое вектора будет перемещено (и в этом конкретном случае вектор пуст, в любом случае). В общем, почему это не удается для std::map::emplace? Использование GCC 8.1.

3 ответа

Решение

Подпись map::emplace является

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

а также {} не имеет типа и не может быть выведено.

Вы можете использовать, для emplace:

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

За insert(одна из ее) подписей:

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

так {42, {}} используется для построения std::pair<const int, std::vector<size_t>>,

Подписи за emplace разные.

Для карты

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

против вектора

template< class... Args >
iterator emplace( const_iterator pos, Args&&... args );

в map если компилятор не может вывести Args от {}так что не получается.

Для vector это намного проще, так как первый параметр в v.emplace({});как известно, const_iterator, Так {} может соответствовать построенному по умолчанию итератору.

Компилятор не может вывести тип для {} так как emplace это функция vararg. Введите тип, и он будет работать:

m.emplace(42, (std::vector<size_t>){}); 
Другие вопросы по тегам