Построение вектора на месте из списка инициализации (для класса с аргументами конструктора)

Возможный дубликат:
Можно ли инициализировать списком вектор типа только для перемещения?

Изменить 1: Пожалуйста, рассмотрите вопрос о повторном открытии. Мой вопрос подчеркивает строительство на месте. Перемещение строительство является альтернативой, но не то, о чем этот вопрос. Спасибо за ответы!

Редактировать 2: Поскольку я не могу ответить на этот вопрос (он был закрыт), я публикую свое собственное предложение здесь. Следующее не так хорошо, как ответы, которые я принял, но может быть полезно для других. По крайней мере, только конструктор перемещения называется:

std::vector<A2> vec;
{
std::array<A2,3> numbers{{{2,3},{5,6},{7,8}}};
vec.reserve(numbers.size());
for (auto &v: numbers) vec.emplace_back(std::move(v)) ;
}

Исходное сообщение:

Размышляя об ответе на этот вопрос: Инициализация классов в массиве векторов STL Я обнаружил, что не могу найти способ получить конструкцию вектора на месте из списка инициализации. Что мне не хватает?

Теперь, пытаясь быть более понятным, я хотел бы эту (совершенно правильную) инициализацию

std::vector<A2> k{{2,3},{4,5},{8,9}};

иметь эффект, более похожий на этот:

  std::vector<A2> k2;
k2.reserve(3);
k2.emplace_back(2,3);
k2.emplace_back(4,5);
k2.emplace_back(8,9);

Однако в первом случае копия конструктор вызывается для A2 на время вставки. Есть ли способ избежать этого? Что говорит стандарт?

Я отчаянно пытался

std::vector<A2> k{{2,3},{4,5},std::move(A2{8,9})};

но это порождает дополнительный вызвать конструктор перемещения, чего я тоже не ожидал. Я просто хотел явно намекнуть, что A2 временный, что, как я думал, подразумевается.

Полный пример:

#include <vector>
#include <iostream>

struct A2 {
int mk;
int mj;
A2(int k,int j) : mk(k),mj(j) {
std::cout << "     constr for "<<this<< ":"<< mk<<std::endl;
}
A2(const A2& a2) {
mk=a2.mk;
mj=a2.mj;
std::cout << "copy constr for "<<this<< ":" << mk<<std::endl;
}
A2(A2&& a2) noexcept  {
mk=std::move(a2.mk);
mj=std::move(a2.mj);
std::cout << "move constr for "<<this<< ":"<< mk<<std::endl;
}
};

struct Ano {
Ano() {
std::cout << "     constr for "<<this <<std::endl;
}
Ano(const Ano& ano) {
std::cout << "copy constr for "<<this<<std::endl;
}
Ano(Ano&& ano) noexcept  {
std::cout << "move constr for "<<this<<std::endl;
}
};int main (){
// here both constructor and copy constructor is called:
std::vector<A2> k{{2,3},{4,5},std::move(A2{8,9})};

std::cout << "......"<<std::endl;
std::vector<A2> k2;
k2.reserve(3);
// here (naturally) only constructor is called:
k2.emplace_back(2,3);
k2.emplace_back(4,5);
k2.emplace_back(8,9);

std::cout << "......"<<std::endl;
// here only constructor is called:
std::vector<Ano> anos(3);

}

Выход:

     constr for 0xbf9fdf18:2
constr for 0xbf9fdf20:4
constr for 0xbf9fdf0c:8
move constr for 0xbf9fdf28:8
copy constr for 0x90ed008:2
copy constr for 0x90ed010:4
copy constr for 0x90ed018:8
......
constr for 0x90ed028:2
constr for 0x90ed030:4
constr for 0x90ed038:8
......
constr for 0x90ed048
constr for 0x90ed049
constr for 0x90ed04a

9

Решение

Список инициализация std::vector в вашем фрагменте ничем не отличается от выполнения следующих действий (если initializer_list имел открытый неявный конструктор или std::vector принял ссылку на массив.):

// directly construct with the backing array of 'initializer_list'
std::vector<A2> v(alias<A2[]>{ A2(2,3), A2(4,5), A2(8,9) });

Это не особый способ построить std::vector это может воспользоваться реализацией, действительно. Инициализация списков — это универсальный способ «равномерной» инициализации типов. Таким образом, нет никакого способа, которым это могло бы наступить std::vector отличается от любого другого определенного пользователем типа. Таким образом, о наличии конструкции в OP do emplace конструкция не может быть и речи.

Теперь поддерживающий массив (или любой константный массив) может быть помещен реализацией в постоянную память, вот почему

std::initializer_list<T>::iterator

просто

typedef T const* iterator;

Так что выходя из std::initializer_list Также не может быть и речи.

Теперь есть ли решение? Да, есть, и это довольно просто, на самом деле!

Мы хотим иметь бесплатную функцию, которая принимает контейнер и количество кортежей, равное количеству элементов, которые вы хотите разместить. Кортежи содержат аргументы конструктора типа контейнера. Легко в теории, легко на практике с трюк с индексами (где indices == seq а также build_indices == gen_seq в коде):

#include <type_traits>
#include <tuple>
#include <utility>

template<class T> using alias = T;
template<class T> using RemoveRef = typename std::remove_reference<T>::type;

template<class C, unsigned... Is, class Tuple>
void emplace_back_one(C& c, seq<Is...>, Tuple&& ts){
c.emplace_back(std::get<Is>(std::forward<Tuple>(ts))...);
}

template<class T> using Size = std::tuple_size<RemoveRef<T>>;

template<class C, class... Tuples>
void emplace_back(C& c, Tuples&&... ts){
c.reserve(sizeof...(Tuples));
alias<char[]>{(
emplace_back_one(c, gen_seq<std::tuple_size<RemoveRef<Tuples>>::value>{}, std::forward<Tuples>(ts))
, '0')...};
}

Живой пример с реализацией seq а также gen_seq,

Код выше звонков emplace_back_one именно так sizeof...(Tuples) время, передавая по одному кортежу за раз в том порядке, в котором emplace_back, Этот код также упорядочен слева направо, что означает, что конструкторы вызываются в том же порядке, в котором вы передали им кортежи. emplace_back_one затем просто распаковывает кортеж с помощью трюка с индексами и передает аргументы c.emplace_back,

5

Другие решения

Строительство объекта через std::initializer_list ничем не отличается от создания объекта из любого другого объект. std::initializer_list не мистическая, призрачная конструкция; это живой, дышащий объект C ++ (хотя и временный). Как таковой, он подчиняется всем правилам обычной жизни, дыша объектами C ++.

Агрегатная инициализация может эффективно исключить копирование / перемещение, потому что это агрегатная инициализация, просто конструкция времени компиляции. std::vector это много вещей; агрегат и просто конструкция времени компиляции не входят в их число. Таким образом, для того, чтобы он мог инициализировать себя из того, что ему дано, он должен выполнять реальный код C ++, а не вещи времени компиляции. Он должен перебирать каждый элемент initializer_list и либо скопируйте эти значения, либо переместите их. И последнее невозможно, так как std::initializer_list не обеспечиваетconst доступ к своим членам.

Инициализация списка инициализатора предназначена для смотреть как агрегатная инициализация, а не как она. Это стоимость наличия динамической абстракции во время выполнения std::vector,

12

По вопросам рекламы [email protected]