Как инициализировать std::vector из массива C-стиля?

Каков самый дешевый способ инициализации std::vector из массива C-стиля?

Пример. В следующем классе у меня есть vector, но из-за внешних ограничений данные будут переданы как массив C-style:

class Foo {
  std::vector<double> w_;
public:
  void set_data(double* w, int len){
   // how to cheaply initialize the std::vector?
}

Очевидно, я могу вызвать w_.resize(), а затем перебрать элементы или вызвать std::copy(). Есть ли лучшие методы?

Ответ 1

Не забывайте, что вы можете рассматривать указатели как итераторы:

w_.assign(w, w + len);

Ответ 2

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

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

Foo::Foo(double* w, int len) : w_(w, w + len) { }

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

void set_data(double* w, int len)
{
    w_.assign(w, w + len);
}

Ответ 3

Ну, Павел был близок, но есть еще более простое и элегантное решение для инициализации последовательного контейнера из массива style c.

В вашем случае:

w_ (array, std::end(array))
  • массив получит нам указатель на начало массива (не поймал его имя),
  • std:: end (array) предоставит нам итератор в конец массива.

Ответ 4

Вы можете "узнать" размер массива автоматически:

template<typename T, size_t N>
void set_data(const T (&w)[N]){
    w_.assign(w, w+N);
}

Надеемся, вы можете изменить интерфейс на set_data, как указано выше. Он по-прежнему принимает массив C-стиля в качестве первого аргумента. Это просто происходит с помощью ссылки.


Как это работает

[Обновить: см. здесь для более подробной дискуссии об изучении размера]

Вот более общее решение:

template<typename T, size_t N>
void copy_from_array(vector<T> &target_vector, const T (&source_array)[N]) {
    target_vector.assign(source_array, source_array+N);
}

Это работает, потому что массив передается как ссылка на массив. В C/С++ вы не можете передать массив как функцию, вместо этого он будет распадаться на указатель, и вы потеряете размер. Но в С++ вы можете передать ссылку на массив.

Передача массива по ссылке требует, чтобы типы точно совпадали. Размер массива является частью его типа. Это означает, что мы можем использовать параметр шаблона N, чтобы узнать размер для нас.

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

template<typename T, size_t N>
vector<T> convert_array_to_vector(const T (&source_array)[N]) {
    return vector<T>(source_array, source_array+N);
}

Ответ 5

std::vector<double>::assign - путь, потому что он маленький код. Но как это работает? Разве это не изменение размера, а затем копирование? В MS-реализации STL я использую его именно так.

Я боюсь, что не быстрее реализовать (повторно) инициализацию std::vector.