Невозможно emplace_back() использовать инициализатор в фигурных скобках для вектора векторов.

Это несколько связано с предыдущим вопросом, который я задал об использовании emplace_back в векторе пар. emplace_back() vs push_back при вставке пары в стандартный::вектор

Теперь мой вопрос касается использования emplace_back в векторе векторов.

Вот код, о котором я спрашиваю с комментариями

std::vector<std::vector<int>> matrix;

matrix.emplace_back({1,2,3}); //doesn't compile

matrix.emplace_back(1,2,3); //doesn't compile

matrix.push_back({1,2,3}); //works and does what is expected (insert a vector made of {1,2,3} into matrix);

matrix.emplace_back(std::vector<int>{1,2,3});   //works but 
//defeats the purpose of using emplace_back since this makes a copy
//and is thus equivalent to push_back in this case?

matrix.emplace_back(3,2) //this compiles, 
//but it seems to insert a vector of size 3 made of 2s into the matrix. 
//not actually sure why it does this

Итак, исходя из этого, matrix.emplace_back(std::vector<int>{1,2,3}); кажется единственным правильным способом использования std::vector<T>::emplace_back в векторе векторов, но это, похоже, не дает никаких преимуществ по сравнению с push_back. Верно ли мое понимание этого вопроса?

Кроме того, может ли кто-нибудь объяснить, почему matrix.emplace_back(3,2) вставляет в матрицу вектор размера 3, состоящий из 2?


person 24n8    schedule 06.06.2019    source источник
comment
matrix.emplace_back(3,2) использует конструктор vector(std::size_t size, const T& value).   -  person Jarod42    schedule 06.06.2019
comment
@ jarod42 Ой, совсем пропустил.   -  person 24n8    schedule 06.06.2019


Ответы (1)


В этом случае {1, 2, 3} не может быть выведено в initializer_list<int> (это то, что ожидает конструктор vector<int>, который вы хотите использовать). Поэтому вам нужно немного помочь ему:

matrix.emplace_back(initializer_list<int>{1, 2, 3});

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

Что касается эффективности, это:

matrix.emplace_back(vector<int>{1, 2, 3});

строит два вектора. Пустой вектор в matrix, а переданный временный. Временное перемещается в пустой вектор. Так что это не так уж и плохо на самом деле.

Однако это:

matrix.emplace_back(initializer_list<int>{1, 2, 3});

Создает только один вектор, используя конструктор, который принимает initializer_list. Обратите внимание, что здесь не создается «дополнительный» объект initializer_list. Такой объект все равно будет создан при создании любого вектора с использованием фигурной инициализации:

vector<int> vec{1, 2, 3};

Это также создает объект initializer_list, потому что это то, что принимает векторный конструктор.

Что касается того, почему emplace_back(2,3) работает, это потому, что есть векторный конструктор, который принимает размер и значение.

person Nikos C.    schedule 06.06.2019
comment
Несколько дополнений: (1) push_back позволяет вычесть {1,2,3} до initializer_list<int>, как оказалось. Есть ли особая причина, по которой emplace_back также не поддерживает эту функцию? (2) Отличается ли matrix.emplace_back(initializer_list‹int›{1, 2, 3}) от matrix.emplace_back(vector‹int›{1, 2, 3}). Похоже, что при таком способе используется конструктор вектора, а затем конструктор initializer_list, тогда как в вашем случае он просто проходит через конструктор initializer_list. Кажется, ваш способ был бы немного более эффективным? - person 24n8; 06.06.2019
comment
@Imanon У меня нет в голове полных правил дедукции, но я надеюсь, что теперь я достаточно хорошо разъяснил свой ответ. - person Nikos C.; 06.06.2019