Як з'єднати два std::vector
с?
a + b
або a.concat(b)
у стандартній бібліотеці? Можливо, реалізація за замовчуванням була б неоптимальною, але кожне об'єднання масивів не потребує мікрооптимізації
Як з'єднати два std::vector
с?
a + b
або a.concat(b)
у стандартній бібліотеці? Можливо, реалізація за замовчуванням була б неоптимальною, але кожне об'єднання масивів не потребує мікрооптимізації
Відповіді:
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
reserve
на вектор призначення?
vector1.capacity() >= 2 * vector1.size()
. Що нетипово, якщо ви не телефонували std::vector::reserve()
. В іншому випадку вектор буде перерозподілятися, виключаючи ітератори, передані як параметри 2 і 3.
.concat
або що- +=
небудь
Якщо ви використовуєте C ++ 11 і хочете перемістити елементи, а не просто їх копіювання, ви можете використовувати std::move_iterator
разом із вставкою (або копією):
#include <vector>
#include <iostream>
#include <iterator>
int main(int argc, char** argv) {
std::vector<int> dest{1,2,3,4,5};
std::vector<int> src{6,7,8,9,10};
// Move elements from src to dest.
// src is left in undefined but safe-to-destruct state.
dest.insert(
dest.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end())
);
// Print out concatenated vector.
std::copy(
dest.begin(),
dest.end(),
std::ostream_iterator<int>(std::cout, "\n")
);
return 0;
}
Це не буде більш ефективно для прикладу з ints, оскільки переміщення їх не є більш ефективним, ніж їх копіювання, але для структури даних з оптимізованими рухами це може уникнути копіювання непотрібного стану:
#include <vector>
#include <iostream>
#include <iterator>
int main(int argc, char** argv) {
std::vector<std::vector<int>> dest{{1,2,3,4,5}, {3,4}};
std::vector<std::vector<int>> src{{6,7,8,9,10}};
// Move elements from src to dest.
// src is left in undefined but safe-to-destruct state.
dest.insert(
dest.end(),
std::make_move_iterator(src.begin()),
std::make_move_iterator(src.end())
);
return 0;
}
Після переміщення елемент src залишається у невизначеному, але безпечному для руйнування стані, а його колишні елементи були перенесені безпосередньо до нового елемента dest у кінці.
std::move(src.begin(), src.end(), back_inserter(dest))
?
Я б використовував функцію вставки , щось на зразок:
vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Або ви можете використовувати:
std::copy(source.begin(), source.end(), std::back_inserter(destination));
Цей шаблон корисний, якщо два вектори не містять абсолютно одного типу речей, оскільки ви можете використовувати щось замість std :: back_inserter для перетворення з одного типу в інший.
reserve
спочатку телефонувати . Причина std::copy
іноді корисна в тому, якщо ви хочете використовувати щось інше, ніж back_inserter
.
З C ++ 11 я вважаю за краще додати вектор b до:
std::move(b.begin(), b.end(), std::back_inserter(a));
коли a
і b
не перекриваються, і b
більше не використовуватимуться.
Це std::move
з <algorithm>
, а не звичайне std::move
з <utility>
.
insert
способу, який є більш безпечним.
insert()
з move_iterator
с? Якщо так, то як?
std::move
ми тут говоримо, оскільки більшість людей не знають цього перевантаження. Сподіваюся, це поліпшення.
std::vector<int> first;
std::vector<int> second;
first.insert(first.end(), second.begin(), second.end());
Я віддаю перевагу тому, про яке вже говорилося:
a.insert(a.end(), b.begin(), b.end());
Але якщо ви використовуєте C ++ 11, є ще один загальний спосіб:
a.insert(std::end(a), std::begin(b), std::end(b));
Також не є частиною питання, але його бажано використовувати reserve
перед тим, як подати заявку на кращу ефективність. І якщо ви поєднуєте вектор з собою, не резервуючи його, не виходить, так і слід завжди reserve
.
Отже, в основному, що вам потрібно:
template <typename T>
void Append(std::vector<T>& a, const std::vector<T>& b)
{
a.reserve(a.size() + b.size());
a.insert(a.end(), b.begin(), b.end());
}
std::
тоді, коли a
походить тип std
, який перемагає загальний аспект.
Ви повинні використовувати вектор :: insert
v1.insert(v1.end(), v2.begin(), v2.end());
Загальний приріст продуктивності для СЦЕПИТЬ, щоб перевірити розмір векторів. І об'єднайте / вставте менший з більшим.
//vector<int> v1,v2;
if(v1.size()>v2.size()) {
v1.insert(v1.end(),v2.begin(),v2.end());
} else {
v2.insert(v2.end(),v1.begin(),v1.end());
}
v1.insert(v2.end()...
використовує ітератор, v2
щоб вказати позицію в v1
.
Якщо ви хочете мати можливість стисло зв'язати вектори, ви можете перевантажити +=
оператора.
template <typename T>
std::vector<T>& operator +=(std::vector<T>& vector1, const std::vector<T>& vector2) {
vector1.insert(vector1.end(), vector2.begin(), vector2.end());
return vector1;
}
Тоді ви можете назвати це так:
vector1 += vector2;
Якщо вас цікавить сильна гарантія виключення (коли конструктор копій може викинути виняток):
template<typename T>
inline void append_copy(std::vector<T>& v1, const std::vector<T>& v2)
{
const auto orig_v1_size = v1.size();
v1.reserve(orig_v1_size + v2.size());
try
{
v1.insert(v1.end(), v2.begin(), v2.end());
}
catch(...)
{
v1.erase(v1.begin() + orig_v1_size, v1.end());
throw;
}
}
Подібне append_move
з високою гарантією взагалі не може бути реалізовано, якщо конструктор переміщення векторного елемента може кинути (що малоймовірно, але все ж).
v1.erase(...
також кидати?
insert
вже справляється з цим. Також цей дзвінок erase
еквівалентний а resize
.
Додайте його до файлу заголовка:
template <typename T> vector<T> concat(vector<T> &a, vector<T> &b) {
vector<T> ret = vector<T>();
copy(a.begin(), a.end(), back_inserter(ret));
copy(b.begin(), b.end(), back_inserter(ret));
return ret;
}
і використовувати його таким чином:
vector<int> a = vector<int>();
vector<int> b = vector<int>();
a.push_back(1);
a.push_back(2);
b.push_back(62);
vector<int> r = concat(a, b);
r міститиме [1,2,62]
Ось загальне рішення із використанням семантики переміщення C ++ 11:
template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, const std::vector<T>& rhs)
{
if (lhs.empty()) return rhs;
if (rhs.empty()) return lhs;
std::vector<T> result {};
result.reserve(lhs.size() + rhs.size());
result.insert(result.cend(), lhs.cbegin(), lhs.cend());
result.insert(result.cend(), rhs.cbegin(), rhs.cend());
return result;
}
template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, const std::vector<T>& rhs)
{
lhs.insert(lhs.cend(), rhs.cbegin(), rhs.cend());
return std::move(lhs);
}
template <typename T>
std::vector<T> concat(const std::vector<T>& lhs, std::vector<T>&& rhs)
{
rhs.insert(rhs.cbegin(), lhs.cbegin(), lhs.cend());
return std::move(rhs);
}
template <typename T>
std::vector<T> concat(std::vector<T>&& lhs, std::vector<T>&& rhs)
{
if (lhs.empty()) return std::move(rhs);
lhs.insert(lhs.cend(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()));
return std::move(lhs);
}
Зверніть увагу, чим це відрізняється від append
ing до a vector
.
Ви можете підготувати власний шаблон для + оператора:
template <typename T>
inline T operator+(const T & a, const T & b)
{
T res = a;
res.insert(res.end(), b.begin(), b.end());
return res;
}
Наступне - просто використовувати +:
vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
cout << x << " ";
cout << endl;
Цей приклад дає вихід:
1 2 3 4 5 6 7 8
T operator+(const T & a, const T & b)
небезпечно, краще використовувати vector<T> operator+(const vector<T> & a, const vector<T> & b)
.
Існує алгоритм std::merge
із C ++ 17 , яка дуже проста у використанні,
Нижче наведено приклад:
#include <iostream>
#include <vector>
#include <algorithm>
int main()
{
//DATA
std::vector<int> v1{2,4,6,8};
std::vector<int> v2{12,14,16,18};
//MERGE
std::vector<int> dst;
std::merge(v1.begin(), v1.end(), v2.begin(), v2.end(), std::back_inserter(dst));
//PRINT
for(auto item:dst)
std::cout<<item<<" ";
return 0;
}
std::vector::insert
, але це робить щось інше: об'єднання двох діапазонів у новий діапазон проти вставлення одного вектора в кінці іншого. Варто згадати у відповіді?
Якщо ваша мета - просто перебрати значення діапазону значень для цілей лише для читання, альтернатива - обернути обидва вектори навколо проксі (O (1)), а не копіювати їх (O (n)), щоб вони були негайно помічені як єдиний, суміжний.
std::vector<int> A{ 1, 2, 3, 4, 5};
std::vector<int> B{ 10, 20, 30 };
VecProxy<int> AB(A, B); // ----> O(1)!
for (size_t i = 0; i < AB.size(); i++)
std::cout << AB[i] << " "; // ----> 1 2 3 4 5 10 20 30
Докладнішу інформацію див. На https://stackoverflow.com/a/55838758/2379625 , включаючи реалізацію "VecProxy", а також плюси та мінуси.
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
Я реалізував цю функцію, яка об'єднує будь-яку кількість контейнерів, переходячи від rvalue-посилань і копіюючи інакше
namespace internal {
// Implementation detail of Concatenate, appends to a pre-reserved vector, copying or moving if
// appropriate
template<typename Target, typename Head, typename... Tail>
void AppendNoReserve(Target* target, Head&& head, Tail&&... tail) {
// Currently, require each homogenous inputs. If there is demand, we could probably implement a
// version that outputs a vector whose value_type is the common_type of all the containers
// passed to it, and call it ConvertingConcatenate.
static_assert(
std::is_same_v<
typename std::decay_t<Target>::value_type,
typename std::decay_t<Head>::value_type>,
"Concatenate requires each container passed to it to have the same value_type");
if constexpr (std::is_lvalue_reference_v<Head>) {
std::copy(head.begin(), head.end(), std::back_inserter(*target));
} else {
std::move(head.begin(), head.end(), std::back_inserter(*target));
}
if constexpr (sizeof...(Tail) > 0) {
AppendNoReserve(target, std::forward<Tail>(tail)...);
}
}
template<typename Head, typename... Tail>
size_t TotalSize(const Head& head, const Tail&... tail) {
if constexpr (sizeof...(Tail) > 0) {
return head.size() + TotalSize(tail...);
} else {
return head.size();
}
}
} // namespace internal
/// Concatenate the provided containers into a single vector. Moves from rvalue references, copies
/// otherwise.
template<typename Head, typename... Tail>
auto Concatenate(Head&& head, Tail&&... tail) {
size_t totalSize = internal::TotalSize(head, tail...);
std::vector<typename std::decay_t<Head>::value_type> result;
result.reserve(totalSize);
internal::AppendNoReserve(&result, std::forward<Head>(head), std::forward<Tail>(tail)...);
return result;
}
Якщо те, що ви шукаєте, це спосіб додавання вектора до іншого після створення, vector::insert
- це найкраща ставка, на яку було відповідено кілька разів, наприклад:
vector<int> first = {13};
const vector<int> second = {42};
first.insert(first.end(), second.cbegin(), second.cend());
На жаль, немає способу побудувати const vector<int>
, як вище, ви повинні побудувати і потім insert
.
Якщо ви насправді шукаєте контейнер для з'єднання цих двох vector<int>
с, можливо, вам стане щось краще, якщо:
vector
є примітивиconst
контейнерЯкщо вищезазначене все вірно, я б запропонував використовувати, basic_string
хто char_type
відповідає розміру примітиву, що міститься у вашому vector
. Ви повинні включити static_assert
у свій код, щоб підтвердити, що ці розміри залишаються послідовними:
static_assert(sizeof(char32_t) == sizeof(int));
З цим правдою ви просто можете:
const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());
Більш детальну інформацію про відмінності між string
та vector
можна подивитися тут: https://stackoverflow.com/a/35558008/2642059
Живий приклад цього коду ви можете подивитися тут: http://ideone.com/7Iww3I
Це рішення може бути дещо складним, але boost-range
може запропонувати ще деякі приємні речі.
#include <iostream>
#include <vector>
#include <boost/range/algorithm/copy.hpp>
int main(int, char**) {
std::vector<int> a = { 1,2,3 };
std::vector<int> b = { 4,5,6 };
boost::copy(b, std::back_inserter(a));
for (auto& iter : a) {
std::cout << iter << " ";
}
return EXIT_SUCCESS;
}
Найчастіше цією метою є поєднання вектора a
і b
просто повторення над ним, виконуючи якусь операцію. У цьому випадку є смішна проста join
функція.
#include <iostream>
#include <vector>
#include <boost/range/join.hpp>
#include <boost/range/algorithm/copy.hpp>
int main(int, char**) {
std::vector<int> a = { 1,2,3 };
std::vector<int> b = { 4,5,6 };
std::vector<int> c = { 7,8,9 };
// Just creates an iterator
for (auto& iter : boost::join(a, boost::join(b, c))) {
std::cout << iter << " ";
}
std::cout << "\n";
// Can also be used to create a copy
std::vector<int> d;
boost::copy(boost::join(a, boost::join(b, c)), std::back_inserter(d));
for (auto& iter : d) {
std::cout << iter << " ";
}
return EXIT_SUCCESS;
}
Для великих векторів це може бути перевагою, оскільки немає копіювання. Він також може бути використаний для легкого копіювання узагальнень у більш ніж один контейнер.
Чомусь немає нічого подібного boost::join(a,b,c)
, що могло б бути розумним.
Це можна зробити за допомогою попередньо реалізованих алгоритмів STL, використовуючи шаблон для використання поліморфного типу.
#include <iostream>
#include <vector>
#include <algorithm>
template<typename T>
void concat(std::vector<T>& valuesa, std::vector<T>& valuesb){
for_each(valuesb.begin(), valuesb.end(), [&](int value){ valuesa.push_back(value);});
}
int main()
{
std::vector<int> values_p={1,2,3,4,5};
std::vector<int> values_s={6,7};
concat(values_p, values_s);
for(auto& it : values_p){
std::cout<<it<<std::endl;
}
return 0;
}
Ви можете очистити другий вектор, якщо не хочете його більше використовувати ( clear()
метод).
З’єднайте два std::vector-s
з for
петлею в однуstd::vector
.
Приклад:
std::vector <int> v1 {1, 2, 3};//declare vector1
std::vector <int> v2 {4, 5};//declare vector2
std::vector <int> suma;//declare vector suma
for(auto i = 0; i < v1.size()-1;i++)//for loop 1
{
suma.push_back(v1[i]);
}
for(auto i = 0; i< v2.size()-1;i++)/for loop 2
{
suma.push_back(v2[i]);
}
for(auto i = 0; i < suma.size(); i++)//for loop 3-output
{
std::cout<<suma[i];
}
Напишіть цей код у main()
.
for
петлі помиляєтесь. Дійсні індекси у векторі від 0 до size()-1
. Ви повинні виконувати умови припинення циклу i < v1.size()
, <
не використовуючи <=
. Використання неправильного стану отримує доступ до пам'яті поза контейнером.
auto
ітератори замість ручної індексації. Вам байдуже, який індекс ви об'єднує лише те, що це робиться послідовно.
size()-1
в двох своїх умовах циклу? Це пропускає останні векторні елементи. Третя петля - єдино правильна зараз.
Якщо чесно, ви можете швидко об'єднати два вектори, скопіювавши елементи з двох векторів в інший або просто додати один з двох векторів !. Це залежить від вашої мети.
Спосіб 1: Призначте новий вектор за його розміром - це сума розміру двох вихідних векторів.
vector<int> concat_vector = vector<int>();
concat_vector.setcapacity(vector_A.size() + vector_B.size());
// Loop for copy elements in two vectors into concat_vector
Спосіб 2: Додайте вектор A шляхом додавання / вставки елементів вектора В.
// Loop for insert elements of vector_B into vector_A with insert()
function: vector_A.insert(vector_A .end(), vector_B.cbegin(), vector_B.cend());
std::move_iterator
так, щоб елементи переміщувалися замість скопійованих. (див. en.cppreference.com/w/cpp/iterator/move_iterator ).
setcapacity
? Що таке function:
?
resize
метод.