Могу ли я инициализировать std :: vector по списку с идеальной пересылкой элементов?

Я заметил, что инициализация aggregate list для std :: vector выполняет инициализацию копирования, когда более применимо перемещение. В то же время несколько emplace_backs делают то, что я хочу.

Я мог придумать только это несовершенное решение - написать функцию-шаблон init_emplace_vector. Однако это оптимально только для неявных конструкторов с одним значением.

template <typename T, typename... Args>
std::vector<T> init_emplace_vector(Args&&... args)
{
  std::vector<T> vec;
  vec.reserve(sizeof...(Args));  // by suggestion from user: eerorika
  (vec.emplace_back(std::forward<Args>(args)), ...);  // C++17
  return vec;
}

Вопрос

Мне действительно нужно использовать emplace_back, чтобы инициализировать std :: vector как можно эффективнее?

// an integer passed to large is actually the size of the resource
std::vector<large> v_init {
  1000,  // instance of class "large" is copied
  1001,  // copied
  1002,  // copied
};

std::vector<large> v_emplaced;
v_emplaced.emplace_back(1000);  // moved
v_emplaced.emplace_back(1001);  // moved
v_emplaced.emplace_back(1002);  // moved

std::vector<large> v_init_emplace = init_emplace_vector<large>(
  1000,   // moved
  1001,   // moved
  1002    // moved
);

Вывод

Класс large выдает информацию о копиях / перемещениях (реализация ниже), поэтому результат моей программы:

- initializer
large copy
large copy
large copy
- emplace_back
large move
large move
large move
- init_emplace_vector
large move
large move
large move

Реализация большого класса

Моя реализация large - это просто копируемый / перемещаемый тип, содержащий большой ресурс, который предупреждает о копировании / перемещении.

struct large
{
  large(std::size_t size) : size(size), data(new int[size]) {}

  large(const large& rhs) : size(rhs.size), data(new int[rhs.size])
  {
    std::copy(rhs.data, rhs.data + rhs.size, data);
    std::puts("large copy");
  }

  large(large&& rhs) noexcept : size(rhs.size), data(rhs.data)
  {
    rhs.size = 0;
    rhs.data = nullptr;
    std::puts("large move");
  }

  large& operator=(large rhs) noexcept
  {
    std::swap(*this, rhs);
    return *this;
  }

  ~large() { delete[] data; }

  int* data;
  std::size_t size;
};

Редактировать

Используя резерв, нет копирования или перемещения. Вызывается только large::large(std::size_t) конструктор. Истинное место.


person reconn    schedule 08.01.2020    source источник
comment
Это не агрегатная инициализация, вы вызываете конструктор, который принимает std::initializer_list.   -  person super    schedule 09.01.2020
comment
Конструкторы std :: vector - это запутанный беспорядок, ИМХО, и на вашем месте я бы действительно избегал этого, если мне это абсолютно не нужно. Кроме того, если вы заранее знаете содержимое векторов (что, кажется, может быть так) - рассмотрите вариант std::array.   -  person einpoklum    schedule 09.01.2020
comment
operator=, который уничтожает источник, довольно необычен и может вызвать неожиданные проблемы.   -  person alain    schedule 09.01.2020
comment
init_emplace_vector можно улучшить с помощью vec.reserve(sizeof...(Args))   -  person Indiana Kernick    schedule 09.01.2020
comment
@alain operator= не уничтожает источник. Это идиома копирования-подмены.   -  person reconn    schedule 09.01.2020
comment
Клянусь, я видел здесь large& operator=(large& rhs), но теперь его нет в истории изменений. Да, large& operator=(large rhs), конечно, в порядке.   -  person alain    schedule 09.01.2020


Ответы (1)


Могу ли я агрегировать-инициализировать std :: vector ...

Нет. std::vector не является агрегатом, поэтому его нельзя инициализировать агрегатом.

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

Могу ли я [list-initialise] std :: vector с идеальной пересылкой элементов?

Нет. Инициализация списка использует std::initializer_list конструктор, а std::initializer_list копирует его аргументы.

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

person eerorika    schedule 09.01.2020