Wie verkette ich zwei std::vector
s?
a + b
oder a.concat(b)
in der Standardbibliothek implementiert ist ? Vielleicht wäre die Standardimplementierung suboptimal, aber jede Array-Verkettung muss nicht mikrooptimiert werden
Wie verkette ich zwei std::vector
s?
a + b
oder a.concat(b)
in der Standardbibliothek implementiert ist ? Vielleicht wäre die Standardimplementierung suboptimal, aber jede Array-Verkettung muss nicht mikrooptimiert werden
Antworten:
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
reserve
zuerst den Zielvektor aufzurufen ?
vector1.capacity() >= 2 * vector1.size()
. Was untypisch ist, wenn Sie nicht angerufen haben std::vector::reserve()
. Andernfalls wird der Vektor neu zugewiesen, wodurch die als Parameter 2 und 3 übergebenen Iteratoren ungültig werden.
.concat
oder +=
oder so
Wenn Sie C ++ 11 verwenden und die Elemente verschieben möchten, anstatt sie nur zu kopieren, können Sie sie std::move_iterator
zusammen mit Einfügen (oder Kopieren) verwenden:
#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;
}
Dies ist für das Beispiel mit Ints nicht effizienter, da das Verschieben nicht effizienter ist als das Kopieren. Bei einer Datenstruktur mit optimierten Verschiebungen kann jedoch das Kopieren unnötiger Zustände vermieden werden:
#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;
}
Nach dem Verschieben bleibt das Element von src in einem undefinierten, aber zerstörungssicheren Zustand, und seine früheren Elemente wurden am Ende direkt auf das neue Element von dest übertragen.
std::move(src.begin(), src.end(), back_inserter(dest))
?
Ich würde die Einfügefunktion verwenden , so etwas wie:
vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Oder Sie könnten verwenden:
std::copy(source.begin(), source.end(), std::back_inserter(destination));
Dieses Muster ist nützlich, wenn die beiden Vektoren nicht genau den gleichen Typ enthalten, da Sie anstelle von std :: back_inserter etwas verwenden können, um von einem Typ in den anderen zu konvertieren.
reserve
zuerst anzurufen . Der Grund std::copy
ist manchmal nützlich, wenn Sie etwas anderes als verwenden möchten back_inserter
.
Mit C ++ 11 würde ich es vorziehen, den Vektor b an a anzuhängen:
std::move(b.begin(), b.end(), std::back_inserter(a));
wann a
und b
nicht überlappen und b
nicht mehr verwendet werden.
Dies ist std::move
von <algorithm>
, nicht das übliche std::move
von <utility>
.
insert
Weg zuwenden, der sicherer ist.
insert()
bei move_iterator
s? Wenn das so ist, wie?
std::move
wir hier sprechen, da die meisten Leute diese Überlastung nicht kennen. Hoffe es ist eine Verbesserung.
std::vector<int> first;
std::vector<int> second;
first.insert(first.end(), second.begin(), second.end());
Ich bevorzuge eine, die bereits erwähnt wird:
a.insert(a.end(), b.begin(), b.end());
Wenn Sie jedoch C ++ 11 verwenden, gibt es noch einen allgemeineren Weg:
a.insert(std::end(a), std::begin(b), std::end(b));
Auch nicht Teil einer Frage, aber es ist ratsam, reserve
vor dem Anhängen zu verwenden , um eine bessere Leistung zu erzielen. Und wenn Sie den Vektor mit sich selbst verketten, ohne ihn zu reservieren, schlägt dies fehl, also sollten Sie es immer tun reserve
.
Also im Grunde was Sie brauchen:
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::
wenn der Typ von a
stammt std
, wodurch der generische Aspekt zunichte gemacht wird .
Sie sollten vector :: insert verwenden
v1.insert(v1.end(), v2.begin(), v2.end());
Eine allgemeine Leistungssteigerung für die Verkettung besteht darin, die Größe der Vektoren zu überprüfen. Und füge den kleineren mit dem größeren zusammen.
//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()...
verwendet einen Iterator in v2
, um die Position in anzugeben v1
.
Wenn Sie Vektoren präzise verketten möchten, können Sie den +=
Operator überladen .
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;
}
Dann können Sie es so nennen:
vector1 += vector2;
Wenn Sie an einer starken Ausnahmegarantie interessiert sind (wenn der Kopierkonstruktor eine Ausnahme auslösen kann):
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;
}
}
Ähnliches append_move
mit starker Garantie kann im Allgemeinen nicht implementiert werden, wenn der Verschiebungskonstruktor des Vektorelements werfen kann (was unwahrscheinlich, aber immer noch ist).
v1.erase(...
werfen?
insert
kümmert sich schon darum. Außerdem entspricht dieser Aufruf erase
von a resize
.
Fügen Sie dieses Ihrer Header-Datei hinzu:
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;
}
und benutze es so:
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 enthält [1,2,62]
Hier ist eine Allzwecklösung mit C ++ 11-Verschiebungssemantik:
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);
}
Beachten Sie, wie sich dies von append
ing zu a unterscheidet vector
.
Sie können Ihre eigene Vorlage für den Operator + vorbereiten:
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;
}
Als nächstes - benutze einfach +:
vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
cout << x << " ";
cout << endl;
Dieses Beispiel gibt folgende Ausgabe:
1 2 3 4 5 6 7 8
T operator+(const T & a, const T & b)
ist gefährlich, es ist besser zu verwenden vector<T> operator+(const vector<T> & a, const vector<T> & b)
.
Es gibt einen Algorithmus std::merge
aus C ++ 17 , der sehr einfach zu bedienen ist.
Unten ist das Beispiel:
#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
, aber es hat etwas anderes zu tun : zwei Bereiche zu einem neuen Bereich zusammenzuführen, anstatt einen Vektor am Ende eines anderen einzufügen. Erwähnenswert in der Antwort?
Wenn Sie lediglich den Wertebereich für schreibgeschützte Zwecke durchlaufen möchten, besteht eine Alternative darin, beide Vektoren um einen Proxy (O (1)) zu wickeln, anstatt sie zu kopieren (O (n)), damit sie sofort angezeigt werden als eine einzige, zusammenhängende.
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
Siehe https://stackoverflow.com/a/55838758/2379625 für weitere Informationen, einschließlich der ‚VecProxy‘ Implementierung sowie Vor - und Nachteile.
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
Ich habe diese Funktion implementiert, die eine beliebige Anzahl von Containern verkettet, von rvalue-Referenzen abweicht und auf andere Weise kopiert
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;
}
Wenn Sie nach einer Möglichkeit suchen, einen Vektor nach der Erstellung an einen anderen anzuhängen, vector::insert
ist dies die beste Wahl, wie bereits mehrfach beantwortet wurde, zum Beispiel:
vector<int> first = {13};
const vector<int> second = {42};
first.insert(first.end(), second.cbegin(), second.cend());
Leider gibt es keine Möglichkeit, eine zu konstruieren const vector<int>
, wie oben müssen Sie dann konstruieren insert
.
Wenn Sie tatsächlich nach einem Container suchen, der die Verkettung dieser beiden vector<int>
s enthält, steht Ihnen möglicherweise etwas Besseres zur Verfügung, wenn:
vector
enthält Grundelementeconst
ContainerWenn alles oben Genannte zutrifft, würde ich vorschlagen, die basic_string
Who's zu char_type
verwenden, die der Größe des in Ihrem enthaltenen Primitivs entsprechen vector
. Sie sollten ein static_assert
in Ihren Code aufnehmen, um zu überprüfen, ob diese Größen konsistent bleiben:
static_assert(sizeof(char32_t) == sizeof(int));
Wenn dies gilt, können Sie einfach Folgendes tun:
const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());
Weitere Informationen zu den Unterschieden zwischen string
und finden vector
Sie hier: https://stackoverflow.com/a/35558008/2642059
Ein Live-Beispiel für diesen Code finden Sie hier: http://ideone.com/7Iww3I
Diese Lösung mag etwas kompliziert sein, hat aber boost-range
auch einige andere nette Dinge zu bieten.
#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;
}
Oft besteht die Absicht darin, den Vektor zu kombinieren a
und b
ihn bei einer Operation einfach zu durchlaufen. In diesem Fall gibt es die lächerliche einfache join
Funktion.
#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;
}
Für große Vektoren kann dies ein Vorteil sein, da nicht kopiert wird. Es kann auch zum Kopieren und Verallgemeinern in mehrere Container verwendet werden.
Aus irgendeinem Grund gibt es nichts Vergleichbares boost::join(a,b,c)
, was vernünftig sein könnte.
Sie können dies mit vorimplementierten STL-Algorithmen tun, indem Sie eine Vorlage für eine polymorphe Verwendung verwenden.
#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;
}
Sie können den zweiten Vektor löschen, wenn Sie ihn nicht weiter verwenden möchten ( clear()
Methode).
Um ehrlich zu sein, können Sie zwei Vektoren schnell verketten, indem Sie Elemente von zwei Vektoren in den anderen kopieren oder nur einen von zwei Vektoren anhängen!. Es hängt von Ihrem Ziel ab.
Methode 1: Zuweisen eines neuen Vektors mit seiner Größe ist die Summe der Größe zweier ursprünglicher Vektoren.
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
Methode 2: Fügen Sie Vektor A hinzu, indem Sie Elemente von Vektor B hinzufügen / einfügen.
// 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
so zu verwenden , dass Elemente verschoben anstatt kopiert werden. (Siehe en.cppreference.com/w/cpp/iterator/move_iterator ).
setcapacity
? Was ist function:
?
resize
Methode.