Замена вектора с помощью списка инициализаторов

i имеет std::vector<std::vector<double>> и хотел бы добавить некоторые элементы в конце этого, так что это было мое испытание:

std::vector<std::vector<double> > vec;
vec.emplace_back({0,0});

но это не скомпилируется, тогда как следующее:

std::vector<double> vector({0,0});

Почему emplace_back не может создать элемент в этой позиции? Или что я делаю неправильно?

Спасибо за вашу помощь.

Ответ 1

Вычисление шаблона не может угадать, что заключенный в скобки список инициализации должен быть вектором. Вы должны быть явными:

vec.emplace_back(std::vector<double>{0.,0.});

Обратите внимание, что это создает вектор, а затем перемещает его в новый элемент с помощью std::vector конструктор перемещения копии. Так что в данном конкретном случае он не имеет преимущества перед push_back(). Ответ @TimKuipers показывает способ обойти эту проблему.

Ответ 2

В предыдущем ответе вы можете получить код для компиляции, когда вы конструируете вектор в строке и замените это. Это означает, однако, что вы вызываете конструктор move на временном векторе, что означает, что вы не создаете вектор на месте, тогда как вся причина использования emplace_back, а не push_back.

Вместо этого вы должны перечислить список инициализаторов в initializer_list, например:

#include <vector>
#include <initializer_list>

int main()
{
    std::vector<std::vector<int>> vec;
    vec.emplace_back((std::initializer_list<int>){1,2});
}

Ответ 3

Конструктор std::vector<double> может использовать фигурный список int для своего конструктора std::vector<double>(std::initializer_list<double>).

emplace_back() не может emplace_back() элемент из выражения фигурных скобок, потому что это шаблон, который использует идеальную пересылку. Стандарт запрещает компилятору выводить тип {0,0}, поэтому std::vector<double>::emplace_back<std::initializer_list<double>>(std::initializer_list<double>) не компилируется для emplace_back({}).

Другой ответ указывает, что emplace_back может быть скомпилирован для аргумента типа std::initializer_list<T> если он не должен выводить тип непосредственно из выражения {}. В качестве альтернативы приведению аргумента к emplace_back, вы можете сначала emplace_back аргумент. Как указано в пункте 30 Мейерса ("Эффективное современное" C++), auto разрешается выводить тип выражения фигурной скобки, а при совершенной пересылке - тип объекта, тип которого был выведен auto.

std::vector<std::vector<double> > vec;
auto int_list = {0, 0}; // int_list is type std::initializer_list<int>
vec.emplace_back(int_list); // instantiates vec.emplace_back<std::initializer_list<int>>

emplace_back добавляет элемент в vec, вызывая std::vector<double>(std::forward<std::initializer_list<int>>(int_list)), что вызывает std::vector<double>(std::initializer_list<double>) конструктор и элементы int_list конвертируются.