Cum pot concatena două std::vector?
Dacă sunteți folosind C++11, și doriți să mutați elemente, mai degrabă decât pur și simplu a le copia, puteți utiliza std::move_iterator
împreună cu bucșa (sau copie):
#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;
}
Acest lucru nu va fi mai eficient pentru exemplul cu int, deoarece se deplasează ei nu este mai eficient decât copierea lor, dar pentru o structură de date cu optimizat, se misca, se poate evita copierea inutile de stat:
#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;
}
După mutare, src's element este lăsat într-un nedefinit dar în condiții de siguranță-pentru a-distrugere de stat, și fostul său elemente au fost transferate direct la dest's nou element la sfârșitul anului.
Mi-ar folosi insert function, ceva de genul:
vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Sau ai putea folosi:
std::copy(source.begin(), source.end(), std::back_inserter(destination));
Acest model este util dacă cei doi vectori don't conțin exact același tip de lucru, pentru că puteți folosi ceva în loc de std::back_inserter pentru a converti de la un tip la altul.
Cu C++11, am'd preferă următoarele pentru a adăuga vector b la a:
std::move(b.begin(), b.end(), std::back_inserter(a));
când " a " și " b "nu sunt suprapuse, și" b " nu va fi folosită.
Acest lucru este std::muta
de <algoritm>
, nu usual std::muta
de <utilitate>
.
Prefer unul care este deja menționat:
a.insert(a.end(), b.begin(), b.end());
Dar daca folosesti C++11, există un mod mai generic:
a.insert(std::end(a), std::begin(b), std::end(b));
De asemenea, nu parte dintr-o întrebare, dar este recomandabil să utilizați rezerva
înainte de adăugarea pentru o performanță mai bună. Și dacă sunteți concatenarea vector cu sine, fără rezervă nu reușește, așa că întotdeauna ar trebui să "reserve".
Deci, practic ceea ce ai nevoie:
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());
}
Dacă sunteți interesat în puternic excepție de garanție (când constructor de copiere poate arunca o excepție):
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;
}
}
Similare `append_move cu garanție puternică poate't fi puse în aplicare în general, dacă vectorul element de's muta constructor poate arunca (ceea ce este puțin probabil, dar încă).
Adăugați asta la fișier antet:
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;
}
și de a folosi în felul acesta:
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 va conține [1,2,62]
Aici's un scop general soluție folosind C++11 muta semantica:
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);
}
Notă cum aceasta diferă de la append
ing la un "vector".
Dacă vrei să fii capabil de a înlănțui vectori concis, ai putea supraîncărca +=
operator.
șablon <typename T> std::vector<T>& operator +=(std::vector<T>& vector1, const std::vector<T>& vector2) { vector1.a introduce(vector1.end(), vector2.begin(), vector2.end()); reveni vector1; }
Apoi, puteți suna astfel:
vector1 += vector2;
O general impuls de performanță pentru a înlănțui este de a verifica dimensiunea vectorilor. Și îmbinare/introduce unul mai mic cu unul mai mare.
//vector<int> v1,v2;
if(v1.size()>v2.size()){
v1.insert(v1.end(),v2.begin(),v2.end());
}else{
v1.insert(v2.end(),v1.begin(),v1.end());
}
Dacă obiectivul dvs. este pur și simplu pentru a parcurge intreaga gama de valori pentru read-only scopuri, o alternativă este să-și încheie ambii vectori în jurul valorii de un proxy (O(1)) în loc de copierea lor (O(n)), astfel încât acestea sunt pe plan extern tratate ca un singur, contiguu unul.
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
std::cout << AB[6]; // ----> 20
Se referă la https://stackoverflow.com/a/55838758/2379625 pentru mai multe detalii, inclusiv 'VecProxy' aplicare, precum și a pro & cons.
Vă puteți pregăti propriul șablon pentru operatorul+:
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;
}
Următorul lucru - trebuie doar să utilizați +:
vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
cout << x << " ";
cout << endl;
Acest exemplu oferă ieșire:
1 2 3 4 5 6 7 8
Această soluție ar putea fi un pic complicat, dar boost-gama` a, de asemenea, unele alte lucruri frumoase de oferit.
#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;
}
De multe ori cei care intenția este de a combina vector " a " și " b " doar repeta peste o faci unele de funcționare. În acest caz, nu este ridicol de simplu "se alăture" funcția.
#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;
}
Pentru mari vectori acest lucru ar putea fi un avantaj, deoarece nu există nici o copiere. Acesta poate fi, de asemenea, folosit pentru copierea unui generalizează rapid la mai mult de un container.
Pentru unii motiv, nu este nimic ca `boost::se alăture(a,b,c) care ar putea fi rezonabil.
Am'am implementat această funcție care concateneaza orice număr de containere, trecerea de la rvalue-referințe și copierea altfel
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;
}
Există un algoritm std::merge
de la C++17, care este foarte ușor de utilizat,
Mai jos este un exemplu:
#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;
}