добавить массив в std::vector
От: aay  
Дата: 14.06.06 08:10
Оценка:
Подскажите, плз, как правильно добавить в конец vector'а сразу массив значений. Естественно, что можно добавлять поэлементно с помощью push_back, но сдаётся мне что есть более элегантный способ.
Re: добавить массив в std::vector
От: Аноним  
Дата: 14.06.06 08:16
Оценка:
Здравствуйте, aay, Вы писали:

aay>Подскажите, плз, как правильно добавить в конец vector'а сразу массив значений. Естественно, что можно добавлять поэлементно с помощью push_back, но сдаётся мне что есть более элегантный способ.


#define DIM(arr) (sizeof(arr)/sizeof(arr[0]))

std::vector<int> vi;
int ari[100];
// ...
std::copy( &ari[0], &ari[ DIM(ari)], back_inserter( vi));
Re: добавить массив в std::vector
От: sc Россия  
Дата: 14.06.06 08:17
Оценка:
Здравствуйте, aay, Вы писали:

aay>Подскажите, плз, как правильно добавить в конец vector'а сразу массив значений. Естественно, что можно добавлять поэлементно с помощью push_back, но сдаётся мне что есть более элегантный способ.


    std::vector<int> v;
    v.push_back(1);
    v.push_back(2);
    int m[2] = {3, 4};
    std::copy(m, m + 2, std::back_inserter(v));
    std::copy(v.begin(), v.end(), std::ostream_iterator<int>(std::cout, "\n"));
Re[2]: добавить массив в std::vector
От: aay  
Дата: 14.06.06 08:26
Оценка:
ОК, большое спасибо.
А насколько этот вариант лучше/хуже цикла с push_back'ом?
Re[2]: добавить массив в std::vector
От: Какая разница Украина  
Дата: 14.06.06 08:28
Оценка: 1 (1) :)
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, aay, Вы писали:


aay>>Подскажите, плз, как правильно добавить в конец vector'а сразу массив значений. Естественно, что можно добавлять поэлементно с помощью push_back, но сдаётся мне что есть более элегантный способ.


А>#define DIM(arr) (sizeof(arr)/sizeof(arr[0]))


А>std::vector<int> vi;

А>int ari[100];
А>// ...
А>std::copy( &ari[0], &ari[ DIM(ari)], back_inserter( vi));

еще можно так


vi.insert(vi.end(), ari, ari + DIM(ari));
!0xDEAD
Re[3]: добавить массив в std::vector
От: sc Россия  
Дата: 14.06.06 08:35
Оценка:
Здравствуйте, aay, Вы писали:

aay>ОК, большое спасибо.

aay>А насколько этот вариант лучше/хуже цикла с push_back'ом?
back_inserter эстетичнее (он внутри использует что-то типа cont->push_back, можно самому глянуть, благо исходники stl открыты)
Re[3]: добавить массив в std::vector
От: Кодт Россия  
Дата: 14.06.06 08:56
Оценка: 5 (1) +1
Здравствуйте, aay, Вы писали:

aay>ОК, большое спасибо.

aay>А насколько этот вариант лучше/хуже цикла с push_back'ом?

back_inserter — это тот же самый push_back, только обёрнутый.
Поэтому лучше он только эстетически.
А вот практически — поэлементный push_back массива это зло.

Дело в том, что вектор резервирует под свои элементы место с некоторым запасом (v.capacity() >= v.size()), и когда этот запас исчерпывается — пересоздаёт внутренний массив бОльшего размера... выполняя копирование существующих элементов.
Обычно резервируется полутора-двукратный размер, т.е. v.reserve(v.size()*2), чтобы серия вставок не приводила к переразмещению каждый раз. И тем не менее, в среднем получается log(N) выделений памяти, N копирований; расход памяти — 3N единомоментно (старый массив размера N и новый размера 2N) и 2N постоянно.

Поэтому самый лучший способ — это
v.reserve(N);
v.insert(v.end(), arr, arr+N); // или серией push_back(), это уже не принципиально
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Перекуём баги на фичи!
Re[4]: добавить массив в std::vector
От: Какая разница Украина  
Дата: 14.06.06 09:01
Оценка:
Здравствуйте, Кодт, Вы писали:


К>Поэтому самый лучший способ — это

К>
К>v.reserve(v.size() + N);
К>v.insert(v.end(), arr, arr+N); // или серией push_back(), это уже не принципиально
К>


Маленькая поправка
!0xDEAD
Re[5]: добавить массив в std::vector
От: ArtDenis Россия  
Дата: 14.06.06 09:21
Оценка: 1 (1)
Какая разница пишет:
> К>
> К>v.reserve(v.size() + N);
> К>v.insert(v.end(), arr, arr+N); // или серией push_back(), это уже не принципиально
> К>

push_back() медленнее, т.к.
1. При каждом push_back() происходит проверка, нужно ли увеличивать
внутрунний буфер, что немного замедляет работу
2. Компилятор может очень хорошо с'оптимизировать вызов insert (для
типов, которые допускают побайтное копирование), а push_back — наврядли
Posted via RSDN NNTP Server 2.0
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[6]: добавить массив в std::vector
От: Кодт Россия  
Дата: 14.06.06 10:16
Оценка:
Здравствуйте, ArtDenis, Вы писали:

>> К>v.reserve(v.size() + N);
>> К>v.insert(v.end(), arr, arr+N); // или серией push_back(), это уже не принципиально

AD>push_back() медленнее, т.к.
AD>1. При каждом push_back() происходит проверка, нужно ли увеличивать
AD>внутрунний буфер, что немного замедляет работу
AD>2. Компилятор может очень хорошо с'оптимизировать вызов insert (для
AD>типов, которые допускают побайтное копирование), а push_back — наврядли

Поскольку template<class Iter> void insert(iterator where, Iter src_begin, Iter src_end) — шаблонный, то наивная реализация всё равно превратит его в серию push_back(), а умная — сама сделает однократное reserve.

Примерно так:
template<class Iter> void insert(iterator where, Iter src_begin, Iter src_end)
{
    _insert_num(where-begin(), src_begin, src_end, iterator_traits<_Iter>::iterator_category());
}

// дефолтный (он же наивный) случай
template<class Iter> void _insert_num(size_type where, Iter src_begin, Iter src_end, input_iterator_tag)
{
    for( ; src_begin != src_end; ++where, ++src_begin)
        _insert_num_cnt(where, 1, src_begin);
}
// продвинутый случай
template<class Iter> void _insert_num(size_type where, Iter src_begin, Iter src_end, forward_iterator_tag)
{
    size_type count = distance(src_begin,src_end);
    _insert_num_cnt(where, count, src_begin);
}

// обратите внимание: переход от итератора к смещению (where) связан с тем, что итератор после вставки инвалидируется
// поэтому наивная реализация с её многократной вставкой была бы неработоспособна
template<class Iter> void _insert_num_cnt(size_type where, size_type count, Iter src_begin)
{
    // выполняется (если надо) переразмещение с переносом головной части + перенос хвостовой части
    // затем copy-конструирование новых элементов на освободившемся месте
}

Этот код — по мотивам Dinkumware STL для VC2005.

Отсюда же можно сделать вывод: при работе с чистыми итераторами ввода (например, потоковыми) лучше не вставлять данные сразу в существующий вектор (особенно — если не в конец), а использовать промежуточный вектор.
// нужно:
v.insert(v.begin(), istream_iterator<int>(cin), istream_iterator<int>());

// делаем так:
vector<int> tmp;
tmp.insert(tmp.end(), istream_iterator<int>(cin), istream_iterator<int>()); // N копирований
v.insert(v.begin(), tmp); // сдвиг v.size() элементов и N копирований
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Перекуём баги на фичи!
Re[4]: добавить массив в std::vector
От: pivoo Россия  
Дата: 14.06.06 10:35
Оценка: :)
Здравствуйте, Кодт, Вы писали:

К>Здравствуйте, aay, Вы писали:


aay>>ОК, большое спасибо.

aay>>А насколько этот вариант лучше/хуже цикла с push_back'ом?

К>back_inserter — это тот же самый push_back, только обёрнутый.

К>Поэтому лучше он только эстетически.
К>А вот практически — поэлементный push_back массива это зло.

К>Дело в том, что вектор резервирует под свои элементы место с некоторым запасом (v.capacity() >= v.size()), и когда этот запас исчерпывается — пересоздаёт внутренний массив бОльшего размера... выполняя копирование существующих элементов.

К>Обычно резервируется полутора-двукратный размер, т.е. v.reserve(v.size()*2), чтобы серия вставок не приводила к переразмещению каждый раз. И тем не менее, в среднем получается log(N) выделений памяти, N копирований; расход памяти — 3N единомоментно (старый массив размера N и новый размера 2N) и 2N постоянно.

К>Поэтому самый лучший способ — это

К>
К>v.reserve(N);
К>v.insert(v.end(), arr, arr+N); // или серией push_back(), это уже не принципиально
К>


а resize() + memcpy() не быстрее получится?
не для всех случаев, конечно, подходит...
Re[5]: добавить массив в std::vector
От: ArtDenis Россия  
Дата: 14.06.06 15:36
Оценка:
Здравствуйте, pivoo, Вы писали:

P>а resize() + memcpy() не быстрее получится?

P>не для всех случаев, конечно, подходит...

Точнее resize() + copy(). Это самый быстрый способ для данной задачи
... << RSDN@Home 1.1.4 stable rev. 510>>
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[6]: добавить массив в std::vector
От: Кодт Россия  
Дата: 14.06.06 15:55
Оценка:
Здравствуйте, ArtDenis, Вы писали:

P>>а resize() + memcpy() не быстрее получится?

P>>не для всех случаев, конечно, подходит...

AD>Точнее resize() + copy(). Это самый быстрый способ для данной задачи


Для типов с тривиальным конструктором — это то же самое, что и insert(end(), ...). А для типов с нетривиальным — делаем лишнее конструирование.
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Перекуём баги на фичи!
Re[7]: добавить массив в std::vector
От: ArtDenis Россия  
Дата: 14.06.06 17:05
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Для типов с тривиальным конструктором — это то же самое, что и insert(end(), ...).

Неа. Там нет сдвига элементов. Хотя там ничего не сдвигается (если вставлять в конец вектора), но формально код сдвига присутствует

К>А для типов с нетривиальным — делаем лишнее конструирование.

Да, правильно. Это я упустил из вида...
... << RSDN@Home 1.1.4 stable rev. 510>>
[ 🎯 Дартс-лига Уфы | 🌙 Программа для сложения астрофото ]
Re[7]: добавить массив в std::vector
От: Roman Odaisky Украина  
Дата: 14.06.06 18:34
Оценка:
Здравствуйте, Кодт, Вы писали:

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

К>
// нужно:
v.insert(v.begin(), istream_iterator<int>(cin), istream_iterator<int>());

// делаем так:
vector<int> tmp;
tmp.insert(tmp.end(), istream_iterator<int>(cin), istream_iterator<int>()); // N копирований
v.insert(v.begin(), tmp); // сдвиг v.size() элементов и N копирований

Что-то кажется мне, что при работе с istream_итераторами выжимать миллисекунды из вектора малоосмысленно. Вот если не в конец, то да. И вообще, std::vector MD, std::deque rulez (Sutter). Деки умеют куда эффективнее делать push_back, и умеют работать с другого конца.
До последнего не верил в пирамиду Лебедева.
Re[2]: добавить массив в std::vector
От: Roman Odaisky Украина  
Дата: 14.06.06 18:45
Оценка:
Здравствуйте, Аноним, Вы писали:

А>#define DIM(arr) (sizeof(arr)/sizeof(arr[0]))


А лучше не так, а лучше вот так:
template <std::size_t N>
struct TypeWhichHasSizeExactly
{
    typedef char (&type)[N];
};

template <typename ArrayElementT, std::size_t N>
typename TypeWhichHasSizeExactly<N>::type arrayLengthHelper(ArrayElementT (&)[N]); // определение не требуется

// или 'char (&arrayLengthHelper(ArrayElementT (&)[N]))[N]', но это некрасиво

#define ARRAY_LENGTH(array) sizeof(arrayLengthHelper(array))
До последнего не верил в пирамиду Лебедева.
Re: добавить массив в std::vector
От: seas  
Дата: 15.06.06 10:15
Оценка:
Здравствуйте, aay, Вы писали:

aay>Подскажите, плз, как правильно добавить в конец vector'а сразу массив значений. Естественно, что можно добавлять поэлементно с помощью push_back, но сдаётся мне что есть более элегантный способ.


На самом деле есть книжка 'Effective STL' — там это все обсуждается достаточно хорошо.
Деталей не помню — но кажется смысл таков — для всякого рода вставок лучше использовать методы контейнера нежели алгоритмы и еще чего-то, поскольку методы контейнера знают специфику (непрерывность памяти или связный список) — поэтому могут оптимально делать.

А раз уж говорим про STL (а значит достаточно переносимый способ) — то всякие memcpy — это не то — лучше не пользовать.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.