2017-02-04 3 views
4

Если я зарезервирую некоторое пространство для вектора, а затем скопирую в него значения с std::copy_n(), я получу значения, скопированные правильно и доступные, но размер вектора равен все еще ноль. Это ожидаемое поведение? Должен ли я изменить размер вектора вместо этого, даже если он не так эффективен?std :: copy_n не меняет размер целевого вектора

#include <algorithm> 
#include <iostream> 
#include <vector> 

int main() 
{ 
    std::vector<double> src, dest; 

    for(double x = 0.0; x < 100.0; ++x) 
     src.push_back(x); 

    dest.reserve(src.size()); 

    std::copy_n(src.cbegin(), src.size(), dest.begin()); 

    std::cout << "src.size() = " << src.size() << std::endl; 
    std::cout << "dest.size() = " << dest.size() << std::endl; 

    for(size_t i = 0; i < src.size(); ++i) 
     std::cout << dest[i] << " "; 

} 

Составители испытываться: лязг, GCC, Visual C++

+0

Я думаю, что 'reserve()' влияет только на емкость, а не на размер. –

+0

@RawN, да, но я ожидал, что 'copy_n' обновит размер. – Pietro

+0

Вы знаете, что можете просто сделать dest = src? Вы знаете функцию назначения вектора? –

ответ

5

но размер вектора по-прежнему равна нулю

std::copy_n не изменит размер контейнера, просто скопировать значение и шаг итераторы; он даже не имеет никакой информации о контейнере. Таким образом, код имеет неопределенное поведение, даже если он работает нормально.

Следует ли вместо этого изменить размер вектора, даже если он не так эффективен?

Да, вы можете использовать std::vector::resize вместо std::vector::reserve для решения проблемы. Как вы могли подумать, это означает, что все элементы будут построены на resize во-первых, затем назначаются copy_n.

Вы можете использовать std::back_inserter, который добавит элементы в конце контейнера, вызвав функцию-член push_back() контейнера (т. Е. Непосредственно конструирует элементы), тем самым увеличив размер контейнера. например

dest.reserve(src.size()); 
std::copy_n(src.cbegin(), src.size(), std::back_inserter(dest)); 
+0

Не 'std :: back_inserter' путь медленнее, чем' std :: vetor :: resize', а затем копирование? –

+0

@KamilKoczurek Нет. Он будет ссылаться на 'push_back' контейнера для непосредственного добавления элементов; вместо того, чтобы создавать все элементы с помощью 'resize', а затем назначать их. – songyuanyao

+0

Правильно, я не понимал, что вы называете 'std :: vetor :: reserve', поэтому нет необходимости перераспределять память, что плохо. –

0

СТАНДА :: вектора имеет размера и емкости. Он оставляет больше места, чем необходимо, чтобы сделать вставку быстрее. резерв позволяет указать эту емкость, а изменить размер изменяет реальный размер вектора.

0

Ваша dest выделенная память для хранения элементов после того, как вы вызвали reserve, но он не вызывает конструкторы и фактически пуст, поэтому ваш код приводит к UB. Используйте resize, чтобы создать эти элементы, и тогда все будет в порядке.

dest.resize(src.size()); 
std::copy_n(src.cbegin(), src.size(), dest.begin()); 

std::cout << "src.size() = " << src.size() << std::endl; 
std::cout << "dest.size() = " << dest.size() << std::endl; 

for(size_t i = 0; i < src.size(); ++i) 
    std::cout << dest[i] << " "; 
2

Главное помнить о стандартных алгоритмах библиотеки является то, что они работают на диапазонах, а не контейнеры. Контейнеры являются одним из способов создания диапазонов, но это не единственный способ. Алгоритмы, которые записывают результаты в диапазон, предполагают, что они пишут в действительных местах; они не могут и не могут расширить диапазон, на который они пишут.

Поэтому, когда вы звоните std::copy_n, вы должны предоставить диапазон, достаточно большой для хранения результата. Это означает настройку диапазона с помощью dest.resize(src.size());, а не только выделение памяти dest.reserve(std.size());.

В качестве альтернативы вы можете предоставить диапазон, который знает, что он прикреплен к контейнеру, и ему необходимо настроить размер, вызывая алгоритм с std::back_inserter(dest) вместо dest.begin().

Смежные вопросы