Подтвердить что ты не робот

Emplace_back не работает с std::vector <std:: map <int, int >>

Я пытаюсь сделать emplace_back в std::vector<std::map<int, int>>, но не смог найти правильный синтаксис для этого.

#include<map>
#include<vector>

int main()
{
    std::vector<std::map<int, int>> v;
    std::map<int,int> a {{1,2}};

    v.push_back({{1,2}});

    v.emplace_back({1,2});    // error
    v.emplace_back({{1,2}});  // error
    v.emplace_back(({1,2}));  // error
}

push_back работает здесь, но не emplace_back. Как я могу заставить emplace_back работать?

4b9b3361

Ответ 1

Можно достичь этого, используя вспомогательную функцию следующим образом:

 #include <map>
 #include <vector>

 void emplace_work_around(
    std::vector<std::map<int, int>>& v,
    std::initializer_list<std::pair<const int,int>> && item
 )
 {
    v.emplace_back(std::forward<std::initializer_list<std::pair<const int,int>>>(item));
 }

int main()
{
    std::vector<std::map<int, int>> v;

    emplace_work_around(v,{{1,2}});
}

Проблема заключалась в том, что мы пишем:

v.emplace_back({{1,2}});  // here {{1,2}} does not have a type.

компилятор не может вывести тип аргумента и не может решить, какой конструктор вызвать.

Основная идея заключается в том, что когда вы пишете функцию типа

template<typename T>
void f(T) {}

и используйте его как

f( {1,2,3,4} ); //error

вы получите ошибку компилятора, так как {1,2,3,4} имеет тип.

Но если вы определяете свою функцию как

template<typename T>
void f(std::initializer_list<T>) {}
 f( {1,2,3,4} );

тогда он компилируется отлично.

Ответ 2

emplace_back переводит все аргументы в соответствующий конструктор типа элемента. Теперь std::map имеет конструктор списка инициализаторов, но он ожидает список std::pair<const Key, Value>, т.е. std::pair<const int, int>. push_back не является шаблоном, поэтому он просто ожидает один тип и, таким образом, выполняет преобразование. То есть здесь не происходит никакого вывода.

Вам нужно явно указать, что вы хотите иметь std::pair; должно работать следующее:

#include<map>
#include<vector>

int main()
{
    std::vector<std::map<int, int>> v;

    v.emplace_back(std::initializer_list<std::pair<const int, int>>{
            {1,2},{3,4},{5,6}});

    return 0;
}

По той же причине это не скомпилируется:

    v.emplace_back({std::pair<const int,int>(1,2),
                    std::pair<const int,int>(3,4)});

Это связано с тем, что, хотя список, заключенный в фигурной скобке, может давать список инициализаторов, этого не нужно. Это может быть вызов конструктора или что-то в этом роде. Итак, пишу

auto l = {std::pair<const int,int>(1,2),
          std::pair<const int,int>(3,4)};

выводит список инициализаторов для l, но само выражение может использоваться по-другому:

std::pair<std::pair<const int, int>, std::pair<const int, int>> p =
          {std::pair<const int,int>(1,2),
          std::pair<const int,int>(3,4)}

Весь этот материал становится немного грязным.

В принципе, если у вас есть список, заключенный в скобки, он может выдавать список инициализаторов или вызывать соответствующий конструктор. Бывают случаи, когда компилятор не может определить, какие типы необходимы; emplace_back является одним из них (из-за пересылки). В других случаях это работает, потому что все типы определены в выражении. Например:.

#include <vector>
#include <utility>

int main() 
{
    std::vector<std::pair<const int, int>> v = 
         {{1,2},{3,4},{5,6}};
    return 0;
}

Теперь причина, по которой он не работает, заключается в том, что ни один тип не может быть выведен. То есть emplace_back пытается вывести имя типов ввода, но это невозможно, так как список заключенных в фигурной скобке имеет несколько типов, которые он может описать. Следовательно, не существует соответствующего вызова функции.