Làm thế nào để tôi ghép hai std::vector
s?
a + b
hoặc a.concat(b)
trong thư viện tiêu chuẩn không? Có thể việc triển khai mặc định sẽ không tối ưu, nhưng mọi kết nối mảng không cần phải được tối ưu hóa vi mô
Làm thế nào để tôi ghép hai std::vector
s?
a + b
hoặc a.concat(b)
trong thư viện tiêu chuẩn không? Có thể việc triển khai mặc định sẽ không tối ưu, nhưng mọi kết nối mảng không cần phải được tối ưu hóa vi mô
Câu trả lời:
vector1.insert( vector1.end(), vector2.begin(), vector2.end() );
reserve
vectơ đích trước có hữu ích không?
vector1.capacity() >= 2 * vector1.size()
. Đó là không điển hình trừ khi bạn gọi std::vector::reserve()
. Nếu không, vectơ sẽ phân bổ lại, làm mất hiệu lực các trình vòng lặp được truyền dưới dạng tham số 2 và 3.
.concat
hoặc +=
một cái gì đó
Nếu bạn đang sử dụng C ++ 11 và muốn di chuyển các phần tử thay vì chỉ sao chép chúng, bạn có thể sử dụng std::move_iterator
cùng với insert (hoặc sao chép):
#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;
}
Điều này sẽ không hiệu quả hơn với ví dụ với ints, vì việc di chuyển chúng không hiệu quả hơn so với sao chép chúng, nhưng đối với cấu trúc dữ liệu với các bước di chuyển được tối ưu hóa, nó có thể tránh sao chép trạng thái không cần thiết:
#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;
}
Sau khi di chuyển, phần tử của src bị bỏ lại ở trạng thái không xác định nhưng an toàn để phá hủy và phần tử cũ của nó được chuyển trực tiếp sang phần tử mới của Dest.
std::move(src.begin(), src.end(), back_inserter(dest))
cái gì?
Tôi sẽ sử dụng chức năng chèn , đại loại như:
vector<int> a, b;
//fill with data
b.insert(b.end(), a.begin(), a.end());
Hoặc bạn có thể sử dụng:
std::copy(source.begin(), source.end(), std::back_inserter(destination));
Mẫu này hữu ích nếu hai vectơ không chứa chính xác cùng loại, bởi vì bạn có thể sử dụng thứ gì đó thay vì std :: back_inserter để chuyển đổi từ loại này sang loại khác.
reserve
trước. Lý do std::copy
đôi khi hữu ích là nếu bạn muốn sử dụng một cái gì đó khác hơn back_inserter
.
Với C ++ 11, tôi muốn theo sau để nối vectơ b vào a:
std::move(b.begin(), b.end(), std::back_inserter(a));
khi a
và b
không bị chồng chéo, và b
sẽ không được sử dụng nữa.
Đây là std::move
từ <algorithm>
, không phải từ thông thường .std::move
<utility>
insert
cách cũ sẽ an toàn hơn.
insert()
với move_iterator
s? Nếu vậy thì thế nào?
std::move
chúng ta đang nói ở đây, vì hầu hết mọi người không biết sự quá tải này. Hy vọng nó là một sự cải tiến.
std::vector<int> first;
std::vector<int> second;
first.insert(first.end(), second.begin(), second.end());
Tôi thích một cái đã được đề cập:
a.insert(a.end(), b.begin(), b.end());
Nhưng nếu bạn sử dụng C ++ 11, có một cách chung hơn:
a.insert(std::end(a), std::begin(b), std::end(b));
Ngoài ra, không phải là một phần của câu hỏi, nhưng nên sử dụng reserve
trước khi nối để có hiệu suất tốt hơn. Và nếu bạn đang ghép vector với chính nó, mà không đặt trước nó sẽ thất bại, vì vậy bạn luôn luôn nên reserve
.
Về cơ bản những gì bạn cần:
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::
nếu loại a
xuất phát std
, đánh bại khía cạnh chung.
Bạn nên sử dụng vector :: insert
v1.insert(v1.end(), v2.begin(), v2.end());
Một tăng hiệu suất chung cho concatenate là để kiểm tra kích thước của các vectơ. Và hợp nhất / chèn cái nhỏ hơn với cái lớn hơn.
//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()...
đang sử dụng một trình vòng lặp vào v2
để chỉ định vị trí trong v1
.
Nếu bạn muốn có thể ghép các vectơ một cách chính xác, bạn có thể quá tải +=
toán tử.
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;
}
Sau đó, bạn có thể gọi nó như thế này:
vector1 += vector2;
Nếu bạn quan tâm đến bảo đảm ngoại lệ mạnh (khi trình tạo sao chép có thể ném ngoại lệ):
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;
}
}
Tương tự append_move
với bảo đảm mạnh không thể được thực hiện nói chung nếu hàm tạo di chuyển của phần tử vectơ có thể ném (điều này khó xảy ra nhưng vẫn còn).
v1.erase(...
ném quá?
insert
đã xử lý việc này. Ngoài ra, cuộc gọi erase
này tương đương với a resize
.
Thêm cái này vào tệp tiêu đề của bạn:
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;
}
và sử dụng nó theo cách này:
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 sẽ chứa [1,2,62]
Đây là một giải pháp cho mục đích chung sử dụng ngữ nghĩa di chuyển 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);
}
Lưu ý cách này khác với append
ing đến a vector
.
Bạn có thể chuẩn bị mẫu của riêng bạn cho toán tử +:
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;
}
Điều tiếp theo - chỉ cần sử dụng +:
vector<int> a{1, 2, 3, 4};
vector<int> b{5, 6, 7, 8};
for (auto x: a + b)
cout << x << " ";
cout << endl;
Ví dụ này cho đầu ra:
1 2 3 4 5 6 7 8
T operator+(const T & a, const T & b)
là nguy hiểm, nó là tốt hơn để sử dụng vector<T> operator+(const vector<T> & a, const vector<T> & b)
.
Có một thuật toán std::merge
từ C ++ 17 , rất dễ sử dụng,
Dưới đây là ví dụ:
#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
, nhưng nó lại có một điều khác biệt: hợp nhất hai phạm vi thành một phạm vi mới so với việc chèn một vectơ vào cuối một vectơ khác. Đáng nói trong câu trả lời?
Nếu mục tiêu của bạn chỉ đơn giản là lặp đi lặp lại trong phạm vi các giá trị cho mục đích chỉ đọc, thì một cách khác là bọc cả hai vectơ xung quanh một proxy (O (1)) thay vì sao chép chúng (O (n)), vì vậy chúng sẽ được nhìn thấy ngay lập tức như một người duy nhất, tiếp giáp nhau.
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
Tham khảo https://stackoverflow.com/a/55838758/2379625 để biết thêm chi tiết, bao gồm triển khai 'VecProxy' cũng như ưu và nhược điểm.
vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2 = {11, 12, 13, 14, 15};
copy(v2.begin(), v2.end(), back_inserter(v1));
Tôi đã triển khai chức năng này để nối bất kỳ số lượng container nào, chuyển từ tham chiếu giá trị và sao chép khác
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;
}
Nếu những gì bạn đang tìm kiếm là một cách để nối một vectơ khác sau khi tạo, thì đó vector::insert
là cách tốt nhất của bạn, như đã được trả lời nhiều lần, ví dụ:
vector<int> first = {13};
const vector<int> second = {42};
first.insert(first.end(), second.cbegin(), second.cend());
Đáng buồn thay, không có cách nào để xây dựng một const vector<int>
, như trên bạn phải xây dựng và sau đó insert
.
Nếu những gì bạn thực sự tìm kiếm là một thùng chứa để kết nối hai thứ này vector<int>
, thì có thể có thứ gì đó tốt hơn cho bạn, nếu:
vector
chứa nguyên thủyconst
containerNếu những điều trên đều đúng, tôi khuyên bạn nên sử dụng basic_string
người char_type
phù hợp với kích thước của nguyên thủy chứa trong bạn vector
. Bạn nên bao gồm một static_assert
mã trong mã của mình để xác thực các kích thước này phù hợp:
static_assert(sizeof(char32_t) == sizeof(int));
Với sự nắm giữ này, bạn có thể thực hiện:
const u32string concatenation = u32string(first.cbegin(), first.cend()) + u32string(second.cbegin(), second.cend());
Để biết thêm thông tin về sự khác biệt giữa string
và vector
bạn có thể xem tại đây: https://stackoverflow.com/a/35558008/2642059
Để biết ví dụ trực tiếp về mã này, bạn có thể xem tại đây: http://ideone.com/7Iww3I
Giải pháp này có thể hơi phức tạp, nhưng boost-range
cũng có một số điều tốt đẹp khác để cung cấp.
#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;
}
Thông thường những người có ý định là kết hợp vectơ a
và b
chỉ lặp đi lặp lại qua nó thực hiện một số hoạt động. Trong trường hợp này, có join
chức năng đơn giản vô lý .
#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;
}
Đối với các vectơ lớn, đây có thể là một lợi thế, vì không có sự sao chép. Nó cũng có thể được sử dụng để sao chép một khái quát dễ dàng đến nhiều hơn một container.
Đối với một số lý do không có gì như thế boost::join(a,b,c)
, có thể là hợp lý.
Bạn có thể làm điều đó với các thuật toán STL được triển khai trước bằng cách sử dụng một mẫu để sử dụng kiểu đa hình.
#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;
}
Bạn có thể xóa vectơ thứ hai nếu bạn không muốn sử dụng nó thêm ( clear()
phương thức).
Thành thật mà nói, bạn có thể nối nhanh hai vectơ bằng cách sao chép các phần tử từ hai vectơ sang một vectơ khác hoặc chỉ nối thêm một trong hai vectơ!. Nó phụ thuộc vào mục tiêu của bạn.
Phương pháp 1: Gán vectơ mới với kích thước của nó là tổng của hai vectơ gốc.
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
Phương pháp 2: Nối vectơ A bằng cách thêm / chèn các phần tử của vectơ 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());
std::move_iterator
để các phần tử được di chuyển thay vì sao chép. (xem en.cppreference.com/w/cpp/iterator/move_iterator ).
setcapacity
gì Là function:
gì
resize
phương pháp này.