Khi đọc về C++ 11, tôi có cảm giác rằng khi sử dụng các thùng chứa tiêu chuẩn (như std::vector
) với kiểu dữ liệu người dùng được khuyến khích cung cấp các hoạt động di chuyển noexcept
, nếu có, bởi vì sau đó chỉ có các container sẽ thực sự di chuyển dữ liệu xung quanh thay vì sao chép.Có lợi ích gì từ các hoạt động di chuyển không được chấp nhận khi sử dụng các vùng chứa trong C++ 17 không?
Khi thử điều đó hôm nay, tôi thấy không có sự khác biệt với -std=c++1y
(cho C++ 14) và thậm chí là g ++ - 4,8. Có lẽ tôi đã bỏ lỡ một bản cập nhật trong spec, có thể ví dụ của tôi là sai.
tôi so ba cấu trúc dữ liệu cần được di chuyển với sự khác biệt
- di chuyển theo mặc định bằng cách làm theo "Quy tắc của Zero"
- di chuyển bằng cách cung cấp di chuyển-ops mà không
noexcept
- di chuyển bằng cách cung cấp di chuyển-ops với
noexcept
Khung:
#include <string>
#include <vector>
#include <chrono>
#include <iostream> // cout
using std::vector; using std::cout;
using namespace std::chrono;
long long millisSeit(steady_clock::time_point start) {
return duration_cast<milliseconds>(steady_clock::now()-start).count();
}
namespace {
constexpr size_t ITERATIONS = 1000*1000;
template<typename ELEM>
void timeStuff(std::string name) {
cout << name << "...";
auto start = steady_clock::now();
std::vector<ELEM> data{};
for(size_t idx=0; idx<ITERATIONS; ++idx) {
data.emplace_back(idx % 1719);
}
cout << " " << millisSeit(start) << " ms" << std::endl;
}
}
Với ba kiểu dữ liệu của tôi:
struct RuleOfZeroVector {
std::vector<int> val_;
RuleOfZeroVector(int val) : val_(val, val) {}
};
struct MoveExceptVector {
std::vector<int> val_;
MoveExceptVector(int val) : val_(val, val) {}
MoveExceptVector(MoveExceptVector&& o) /*noexcept*/ : val_{} { swap(val_, o.val_); }
MoveExceptVector& operator=(MoveExceptVector&& o) /*noexcept*/ { swap(val_, o.val_); return *this; }
};
struct MoveNoExceptVector {
std::vector<int> val_;
MoveNoExceptVector(int val) : val_(val, val) {}
MoveNoExceptVector(MoveNoExceptVector&& o) noexcept : val_{} { swap(val_, o.val_); }
MoveNoExceptVector& operator=(MoveNoExceptVector&& o) noexcept { swap(val_, o.val_); return *this; }
};
Và thực hiện các timings:
int main() {
timeStuff<RuleOfZeroVector>("RuleOfZeroVector");
timeStuff<MoveExceptVector>("MoveExceptVector");
timeStuff<MoveNoExceptVector>("MoveNoExceptVector");
}
Với kết quả:
RuleOfZeroVector... 2461 ms
MoveExceptVector... 2472 ms
MoveNoExceptVector... 2468 ms
Như bạn có thể thấy, không có gì khác biệt ce.
Tôi mong đợi MoveExceptVector
chậm hơn nhiều so với hai phương pháp kia, vì tôi giả định vector
sẽ sử dụng nhiều bản sao khi cấu trúc dữ liệu nội bộ phát triển. Sai rồi?
Thêm toán tử ctor/assignment vào 'MoveExceptVector' và bạn sẽ thấy sự khác biệt. – ildjarn
Không cần 'vector :: clear()' để di chuyển dữ liệu xung quanh. Nó đơn giản chạy hàm hủy của mỗi phần tử. 'vector :: push_back' là người thụ hưởng chính của công trình di chuyển' noexcept'. –
"g ++ - 4,8 thậm chí". Loạt 4.8 đã được phát hành vào năm 2013. Đó là eons trước đây. –