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

Рассмотрим следующую попытку замещения пустого 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

Попытка сделать то же самое с вектором векторов работает как положено ( EDIT : не при использовании 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 ]

0 голосов
/ 16 мая 2018

подпись 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>>.

0 голосов
/ 16 мая 2018

Сигнатуры для 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.Таким образом, {} может соответствовать построенному по умолчанию итератору.

0 голосов
/ 16 мая 2018

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

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