Podane odpowiedzi w rzeczywistości nie łączą się. Dołączają kopię. Może być użyteczne (z punktu widzenia wydajności) utworzenie metody konkatenacji std :: vector, jednak wymagałoby to wyrafinowanego współdzielenia zarządzania węzłami i prawdopodobnie dlatego tego nie zrobiono.
FauChristian
8
@FauChristian: Nie, może nie być zastosowania z punktu widzenia wydajności. Pamięć wektorowa musi być ciągła, więc sugerowane przez ciebie jest niemożliwe. Gdybyś chciał „wyrafinowanego współdzielenia zarządzania węzłami” i gdybyś w taki sposób zmienił klasę wektorów, skończyłoby się to deque. Nawet wtedy bardzo trudno jest ponownie wykorzystać pamięć w sugerowany sposób, chociaż zacząłby być nieco bardziej wykonalny. Nie sądzę, że jest obecnie zaimplementowany. Najważniejsze jest to, że w takim współużytkowaniu węzłów zarządzania (deque) węzeł końcowy może być częściowo pusty.
Cookie
4
@lecaruyer Zdajesz sobie sprawę, że właśnie zaznaczyłeś pytanie zadane dwa lata wcześniej jako duplikat
eshirima
9
Czy tylko ja zastanawiam się, dlaczego nie jest to zaimplementowane jako a + blub a.concat(b)w standardowej bibliotece? Być może domyślna implementacja byłaby nieoptymalna, ale każda konkatenacja macierzy nie musi być mikrooptymalizowana
oseiskar
9
lata ewolucji, najbardziej zaawansowane przeciążanie operatorów ze wszystkich języków głównego nurtu, system szablonów podwajający złożoność języka, a jednak odpowiedź nie brzmi v = v1 + v2;
Dodałbym tylko kod, aby najpierw uzyskać liczbę elementów, które zawiera każdy wektor, i ustawić wektor1 jako największy. W przeciwnym razie robisz dużo niepotrzebnego kopiowania.
Joe Pineda
34
Mam pytanie. Czy to zadziała, jeśli wektor1 i wektor2 są tymi samymi wektorami?
Alexander Rafferty,
6
Jeśli łączysz kilka wektorów w jeden, czy pomocne jest reservenajpierw wywołanie wektora docelowego?
Faheem Mitha
33
@AlexanderRafferty: Tylko jeśli vector1.capacity() >= 2 * vector1.size(). Co jest nietypowe, chyba że zadzwoniłeś std::vector::reserve(). W przeciwnym razie wektor przeniesie się ponownie, unieważniając iteratory przekazane jako parametry 2 i 3.
Drew Dormann
28
Szkoda, że w standardowej bibliotece nie ma bardziej zwięzłego wyrażenia. .concatlub +=coś w tym stylu
nmr
193
Jeśli używasz C ++ 11 i chcesz przenieść elementy, a nie tylko je kopiować, możesz użyć std::move_iteratorwraz z wstawką (lub kopią):
#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"));return0;}
Nie będzie to bardziej wydajne w przykładzie z ints, ponieważ przenoszenie ich nie jest bardziej wydajne niż ich kopiowanie, ale w przypadku struktury danych ze zoptymalizowanymi ruchami można uniknąć kopiowania niepotrzebnego stanu:
#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()));return0;}
Po przeniesieniu element src pozostaje w nieokreślonym, ale bezpiecznym stanie do zniszczenia, a jego poprzednie elementy zostały przeniesione bezpośrednio do nowego elementu dest na końcu.
Ten wzór jest przydatny, jeśli dwa wektory nie zawierają dokładnie tego samego rodzaju rzeczy, ponieważ można użyć czegoś zamiast std :: back_inserter do konwersji z jednego typu na drugi.
metoda kopiowania nie jest tak dobrym sposobem. Będzie wywoływał push_back wiele razy, co oznacza, że jeśli trzeba wstawić wiele elementów, może to oznaczać wiele realokacji. lepiej jest użyć wstawki, ponieważ implementacja wektorowa mogłaby przeprowadzić pewną optymalizację, aby uniknąć realokacji. może zarezerwować pamięć przed rozpoczęciem kopiowania
Yogesh Arora
7
@Yogesh: przyznane, ale nic nie stoi na przeszkodzie, aby reservenajpierw zadzwonić . Przyczyna std::copyjest czasami przydatna, jeśli chcesz użyć czegoś innego niż back_inserter.
Roger Lipscombe
Kiedy mówisz „wiele przydziałów”, to prawda - ale liczba przydziałów jest najgorsza w dzienniku (liczba dodanych wpisów) - co oznacza, że koszt dodania wpisu jest stały w liczbie dodanych wpisów. (Zasadniczo nie przejmuj się tym, chyba że profilowanie pokaże, że potrzebujesz rezerwy).
Nieokreślone zachowanie, jeśli rzeczywiście jest b (co jest OK, jeśli wiesz, że to nigdy nie może się zdarzyć - ale warto pamiętać o kodzie ogólnego przeznaczenia).
Martin Bonner wspiera Monikę
1
@MartinBonner Dzięki, że o tym wspomniałeś. Prawdopodobnie powinienem wrócić do starej, insertbezpieczniejszej drogi.
Deqing
15
Ach, INNE std :: move. Całkiem mylące za pierwszym razem, gdy to widzisz.
xaxxon
1
Czy ta różni się od insert()ze move_iterators? Jeśli tak to jak?
GPhilo,
1
Dodałem notatkę o tym, o czym std::movetu mówimy, ponieważ większość ludzi nie zna tego przeciążenia. Mam nadzieję, że to poprawa.
Również nie stanowi części pytania, ale zaleca się użycie reserveprzed dołączeniem w celu uzyskania lepszej wydajności. A jeśli konkatenujesz wektor ze sobą, bez rezerwowania go zawodzi, więc zawsze powinieneś reserve.
@Asu ADL doda tylko, std::jeśli typ apochodzi od std, co eliminuje aspekt ogólny.
Potatoswatter
Słuszna uwaga. w tym przypadku jest to wektor, więc i tak by działało, ale tak, to lepsze rozwiązanie.
Asu
std :: begin () / end () zostały dodane do kolekcji (takich jak tablice), które nie mają ich jako funkcji składowych. Ale tablice również nie mają funkcji członka insert () i wywołuje pytanie „Czy istnieje kolekcja z insertem (), ale bez begin () (która działa ze std :: begin ())?”
Podobne append_movez silną gwarancją nie może być ogólnie zaimplementowane, jeśli konstruktor ruchu elementu wektorowego może rzucać (co jest mało prawdopodobne, ale nadal).
Nie sądzę, żeby było to łatwiejsze w użyciu niż std::vector::insert, ale robi coś innego: łączenie dwóch zakresów w nowy zakres vs wstawianie jednego wektora na końcu innego. Warto wspomnieć w odpowiedzi?
jb
4
Jeśli Twoim celem jest po prostu iteracja w zakresie wartości do celów tylko do odczytu, alternatywą jest zawinięcie obu wektorów wokół proxy (O (1)) zamiast kopiowania ich (O (n)), aby były natychmiast widoczne jako pojedynczy, ciągły.
Ten fragment kodu może rozwiązać problem, ale nie wyjaśnia, dlaczego ani w jaki sposób odpowiada na pytanie. Dołącz wyjaśnienie swojego kodu , ponieważ to naprawdę pomaga poprawić jakość Twojego postu. Osoby zgłaszające / recenzenci: w przypadku odpowiedzi zawierających tylko kod, takich jak ta, głosuj, nie usuwaj! (Uwaga: ta odpowiedź może być w rzeczywistości wystarczająco prosta, aby stanowić wyjaśnienie, a zatem głosować negatywnie, niepotrzebne. Możesz nadal dodać wyjaśnienie, aby zapobiec większej liczbie flag NAA / VLQ.)
Scott Weldon,
2
Zaimplementowałem tę funkcję, która łączy dowolną liczbę kontenerów, przechodząc od referencji do wartości i kopiując w inny sposób
namespaceinternal{// Implementation detail of Concatenate, appends to a pre-reserved vector, copying or moving if// appropriatetemplate<typenameTarget,typenameHead,typename...Tail>voidAppendNoReserve(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");ifconstexpr(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));}ifconstexpr(sizeof...(Tail)>0){AppendNoReserve(target, std::forward<Tail>(tail)...);}}template<typenameHead,typename...Tail>size_tTotalSize(constHead& head,constTail&... tail){ifconstexpr(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<typenameHead,typename...Tail>autoConcatenate(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;}
Jeśli to, czego szukasz, to sposób na dołączenie wektora do innego po utworzeniu, vector::insertto najlepiej postawić zakład, na co kilkakrotnie odpowiadano, na przykład:
vector<int> first ={13};const vector<int> second ={42};
first.insert(first.end(), second.cbegin(), second.cend());
Niestety nie ma sposobu, aby zbudować const vector<int>, ponieważ powyżej musisz zbudować, a następnie insert.
Jeśli to, czego tak naprawdę szukasz, to pojemnik do połączenia tych dwóch vector<int>s, może być dla ciebie coś lepszego, jeśli:
Twój vectorzawiera prymitywy
Zawarte operacje podstawowe mają rozmiar 32-bitowy lub mniejszy
Chcesz constpojemnik
Jeśli wszystkie powyższe są prawdziwe, sugeruję użycie basic_stringkto char_typepasuje do rozmiaru pierwotnej zawartej w twoim vector. static_assertAby sprawdzić, czy te rozmiary są spójne, należy wprowadzić w kodzie a:
To rozwiązanie może być nieco skomplikowane, ale boost-rangema też kilka innych fajnych rzeczy do zaoferowania.
#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;}
Często intencją jest połączenie wektora ai bpo prostu iteracja nad nim wykonując jakąś operację. W tym przypadku istnieje śmieszna prosta joinfunkcja.
#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 iteratorfor(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;}
W przypadku dużych wektorów może to być zaletą, ponieważ nie ma możliwości kopiowania. Można go również wykorzystać do łatwego kopiowania uogólnień do więcej niż jednego kontenera.
Z jakiegoś powodu nie ma czegoś takiego boost::join(a,b,c), co może być rozsądne.
Ci forpętle są błędne. Prawidłowe indeksy w wektorze wynoszą od 0 do size()-1. Musisz zakończyć warunki zakończenia pętli i < v1.size(), używając <nie <=. Zastosowanie złego stanu powoduje dostęp do pamięci poza kontenerem.
Blastfurnace
poza tym nie działa, ten kod jest w dużej mierze nie idiomatyczny. Powinieneś przynajmniej używać autoiteratorów zamiast ręcznego indeksowania. Nie obchodzi Cię, jaki indeks łączysz, tylko że jest on wykonywany sekwencyjnie.
Tarick Welling
Czy możesz wyjaśnić, dlaczego używasz size()-1w dwóch warunkach pętli? To pomija ostatnie elementy wektorowe. Trzecia pętla jest teraz jedyną poprawną.
Blastfurnace
-3
Szczerze mówiąc, możesz szybko połączyć dwa wektory, kopiując elementy z dwóch wektorów do drugiego lub po prostu dołączając jeden z dwóch wektorów !. To zależy od twojego celu.
Metoda 1: Przypisz nowy wektor, którego rozmiar jest sumą rozmiaru dwóch oryginalnych wektorów.
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
Metoda 2: Dołącz wektor A, dodając / wstawiając elementy wektora B.
// 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());
Co dodaje twoja odpowiedź, której jeszcze nie podano w innych odpowiedziach?
Mat
13
@Mat: pogrubione znaki.
marcv81
Jeśli oryginalne wektor (y) nie są już potrzebne, może być lepiej użyć std::move_iterator, aby elementy były przenoszone zamiast kopiowane. (patrz en.cppreference.com/w/cpp/iterator/move_iterator ).
a + b
luba.concat(b)
w standardowej bibliotece? Być może domyślna implementacja byłaby nieoptymalna, ale każda konkatenacja macierzy nie musi być mikrooptymalizowanaOdpowiedzi:
źródło
reserve
najpierw wywołanie wektora docelowego?vector1.capacity() >= 2 * vector1.size()
. Co jest nietypowe, chyba że zadzwoniłeśstd::vector::reserve()
. W przeciwnym razie wektor przeniesie się ponownie, unieważniając iteratory przekazane jako parametry 2 i 3..concat
lub+=
coś w tym styluJeśli używasz C ++ 11 i chcesz przenieść elementy, a nie tylko je kopiować, możesz użyć
std::move_iterator
wraz z wstawką (lub kopią):Nie będzie to bardziej wydajne w przykładzie z ints, ponieważ przenoszenie ich nie jest bardziej wydajne niż ich kopiowanie, ale w przypadku struktury danych ze zoptymalizowanymi ruchami można uniknąć kopiowania niepotrzebnego stanu:
Po przeniesieniu element src pozostaje w nieokreślonym, ale bezpiecznym stanie do zniszczenia, a jego poprzednie elementy zostały przeniesione bezpośrednio do nowego elementu dest na końcu.
źródło
std::move(src.begin(), src.end(), back_inserter(dest))
?Chciałbym użyć funkcji wstawiania , na przykład:
źródło
Lub możesz użyć:
Ten wzór jest przydatny, jeśli dwa wektory nie zawierają dokładnie tego samego rodzaju rzeczy, ponieważ można użyć czegoś zamiast std :: back_inserter do konwersji z jednego typu na drugi.
źródło
reserve
najpierw zadzwonić . Przyczynastd::copy
jest czasami przydatna, jeśli chcesz użyć czegoś innego niżback_inserter
.W C ++ 11 wolałbym, aby dołączyć wektor b do a:
kiedy
a
ib
nie pokrywają się ib
nie będą już używane.To jest
std::move
z<algorithm>
, nie zwyklestd::move
z<utility>
.źródło
insert
bezpieczniejszej drogi.insert()
zemove_iterator
s? Jeśli tak to jak?std::move
tu mówimy, ponieważ większość ludzi nie zna tego przeciążenia. Mam nadzieję, że to poprawa.źródło
Wolę taki, który jest już wspomniany:
Ale jeśli używasz C ++ 11, istnieje jeszcze jeden ogólny sposób:
Również nie stanowi części pytania, ale zaleca się użycie
reserve
przed dołączeniem w celu uzyskania lepszej wydajności. A jeśli konkatenujesz wektor ze sobą, bez rezerwowania go zawodzi, więc zawsze powinieneśreserve
.Więc w zasadzie to, czego potrzebujesz:
źródło
std::
wynika z wyszukiwania zależnego od argumentu .end(a)
to wystarczy.std::
jeśli typa
pochodzi odstd
, co eliminuje aspekt ogólny.Z zasięgiem v3 możesz mieć leniwą konkatenację:
Demo .
źródło
Powinieneś użyć vector :: insert
źródło
Ogólny wzrost wydajności dla Złącz jest sprawdzenie rozmiaru wektorów. I scal / wstaw mniejszy z większym.
źródło
v1.insert(v2.end()...
używa iteratorav2
do określenia pozycji wv1
.Jeśli chcesz mieć możliwość zwięzłego łączenia wektorów, możesz przeciążyć
+=
operatora.Następnie możesz to tak nazwać:
źródło
Jeśli interesuje Cię silna gwarancja wyjątku (gdy konstruktor kopii może zgłosić wyjątek):
Podobne
append_move
z silną gwarancją nie może być ogólnie zaimplementowane, jeśli konstruktor ruchu elementu wektorowego może rzucać (co jest mało prawdopodobne, ale nadal).źródło
v1.erase(...
też rzucać?insert
już to obsługuje. Również to wywołanieerase
jest równoważne zresize
.Dodaj ten do pliku nagłówka:
i użyj tego w ten sposób:
r będzie zawierać [1,2,62]
źródło
Oto rozwiązanie ogólnego zastosowania wykorzystujące semantykę przenoszenia C ++ 11:
Zauważ, że różni się to od
append
ing do avector
.źródło
Możesz przygotować własny szablon dla operatora +:
Następna rzecz - wystarczy użyć +:
Ten przykład podaje dane wyjściowe:
źródło
T operator+(const T & a, const T & b)
jest niebezpieczne, lepiej jest używaćvector<T> operator+(const vector<T> & a, const vector<T> & b)
.Istnieje algorytm
std::merge
z C ++ 17 , który jest bardzo łatwy w użyciu,Poniżej znajduje się przykład:
źródło
std::vector::insert
, ale robi coś innego: łączenie dwóch zakresów w nowy zakres vs wstawianie jednego wektora na końcu innego. Warto wspomnieć w odpowiedzi?Jeśli Twoim celem jest po prostu iteracja w zakresie wartości do celów tylko do odczytu, alternatywą jest zawinięcie obu wektorów wokół proxy (O (1)) zamiast kopiowania ich (O (n)), aby były natychmiast widoczne jako pojedynczy, ciągły.
Więcej informacji, w tym implementację „VecProxy” oraz zalety i wady, można znaleźć na stronie https://stackoverflow.com/a/55838758/2379625 .
źródło
źródło
Zaimplementowałem tę funkcję, która łączy dowolną liczbę kontenerów, przechodząc od referencji do wartości i kopiując w inny sposób
źródło
Jeśli to, czego szukasz, to sposób na dołączenie wektora do innego po utworzeniu,
vector::insert
to najlepiej postawić zakład, na co kilkakrotnie odpowiadano, na przykład:Niestety nie ma sposobu, aby zbudować
const vector<int>
, ponieważ powyżej musisz zbudować, a następnieinsert
.Jeśli to, czego tak naprawdę szukasz, to pojemnik do połączenia tych dwóch
vector<int>
s, może być dla ciebie coś lepszego, jeśli:vector
zawiera prymitywyconst
pojemnikJeśli wszystkie powyższe są prawdziwe, sugeruję użycie
basic_string
ktochar_type
pasuje do rozmiaru pierwotnej zawartej w twoimvector
.static_assert
Aby sprawdzić, czy te rozmiary są spójne, należy wprowadzić w kodzie a:Dzięki temu utrzymaniu prawdy możesz po prostu:
Aby uzyskać więcej informacji na temat różnic między
string
ivector
można zajrzeć tutaj: https://stackoverflow.com/a/35558008/2642059Przykład tego kodu na żywo można znaleźć tutaj: http://ideone.com/7Iww3I
źródło
To rozwiązanie może być nieco skomplikowane, ale
boost-range
ma też kilka innych fajnych rzeczy do zaoferowania.Często intencją jest połączenie wektora
a
ib
po prostu iteracja nad nim wykonując jakąś operację. W tym przypadku istnieje śmieszna prostajoin
funkcja.W przypadku dużych wektorów może to być zaletą, ponieważ nie ma możliwości kopiowania. Można go również wykorzystać do łatwego kopiowania uogólnień do więcej niż jednego kontenera.
Z jakiegoś powodu nie ma czegoś takiego
boost::join(a,b,c)
, co może być rozsądne.źródło
Możesz to zrobić za pomocą wstępnie zaimplementowanych algorytmów STL, używając szablonu do zastosowania typu polimorficznego.
Możesz usunąć drugi wektor, jeśli nie chcesz go dalej używać (
clear()
metoda).źródło
Połącz dwa
std::vector-s
zfor
pętlą w jednymstd::vector
.Przykład:
Wpisz ten kod w
main()
.źródło
for
pętle są błędne. Prawidłowe indeksy w wektorze wynoszą od 0 dosize()-1
. Musisz zakończyć warunki zakończenia pętlii < v1.size()
, używając<
nie<=
. Zastosowanie złego stanu powoduje dostęp do pamięci poza kontenerem.auto
iteratorów zamiast ręcznego indeksowania. Nie obchodzi Cię, jaki indeks łączysz, tylko że jest on wykonywany sekwencyjnie.size()-1
w dwóch warunkach pętli? To pomija ostatnie elementy wektorowe. Trzecia pętla jest teraz jedyną poprawną.Szczerze mówiąc, możesz szybko połączyć dwa wektory, kopiując elementy z dwóch wektorów do drugiego lub po prostu dołączając jeden z dwóch wektorów !. To zależy od twojego celu.
Metoda 1: Przypisz nowy wektor, którego rozmiar jest sumą rozmiaru dwóch oryginalnych wektorów.
Metoda 2: Dołącz wektor A, dodając / wstawiając elementy wektora B.
źródło
std::move_iterator
, aby elementy były przenoszone zamiast kopiowane. (patrz en.cppreference.com/w/cpp/iterator/move_iterator ).setcapacity
? Co to jestfunction:
?resize
metodzie.