Tôi nhận thấy rằng danh sách tổng hợp initalization của std :: vector thực hiện khởi tạo bản sao khi di chuyển được áp dụng nhiều hơn. Đồng thời, nhiều emplace_backs làm những gì tôi muốn.
Tôi chỉ có thể đưa ra giải pháp không hoàn hảo này bằng cách viết một hàm mẫu init_emplace_vector
. Tuy nhiên, nó chỉ tối ưu cho các hàm tạo giá trị đơn không rõ ràng .
template <typename T, typename... Args>
std::vector<T> init_emplace_vector(Args&&... args)
{
std::vector<T> vec;
vec.reserve(sizeof...(Args)); // by suggestion from user: eerorika
(vec.emplace_back(std::forward<Args>(args)), ...); // C++17
return vec;
}
Câu hỏi
Tôi có thực sự cần sử dụng emplace_back để khởi tạo std :: vector hiệu quả nhất có thể không?
// an integer passed to large is actually the size of the resource
std::vector<large> v_init {
1000, // instance of class "large" is copied
1001, // copied
1002, // copied
};
std::vector<large> v_emplaced;
v_emplaced.emplace_back(1000); // moved
v_emplaced.emplace_back(1001); // moved
v_emplaced.emplace_back(1002); // moved
std::vector<large> v_init_emplace = init_emplace_vector<large>(
1000, // moved
1001, // moved
1002 // moved
);
Đầu ra
Lớp large
tạo thông tin về các bản sao / di chuyển (triển khai bên dưới) và vì vậy đầu ra của chương trình của tôi là:
- initializer
large copy
large copy
large copy
- emplace_back
large move
large move
large move
- init_emplace_vector
large move
large move
large move
Thực hiện lớp lớn
Việc triển khai của tôi large
chỉ đơn giản là một loại có thể sao chép / di chuyển được, nắm giữ một nguồn tài nguyên lớn cảnh báo về việc sao chép / di chuyển.
struct large
{
large(std::size_t size) : size(size), data(new int[size]) {}
large(const large& rhs) : size(rhs.size), data(new int[rhs.size])
{
std::copy(rhs.data, rhs.data + rhs.size, data);
std::puts("large copy");
}
large(large&& rhs) noexcept : size(rhs.size), data(rhs.data)
{
rhs.size = 0;
rhs.data = nullptr;
std::puts("large move");
}
large& operator=(large rhs) noexcept
{
std::swap(*this, rhs);
return *this;
}
~large() { delete[] data; }
int* data;
std::size_t size;
};
Biên tập
Bằng cách sử dụng dự trữ, không có bản sao hoặc di chuyển. Chỉ có large::large(std::size_t)
constructor được gọi. Nơi thật sự.
std::array
.
operator=
phá hủy nguồn này khá bất thường và có thể gây ra sự cố không mong muốn.
init_emplace_vector
có thể được cải thiện vớivec.reserve(sizeof...(Args))
operator=
không phá hủy nguồn. Đó là thành ngữ hoán đổi bản sao.
std::initializer_list
.