【发布时间】:2017-02-04 05:54:32
【问题描述】:
在阅读 C++11 时,我有一种感觉,当使用标准容器(如 std::vector)和用户定义的数据类型时,鼓励提供 noexcept 移动操作,如果有的话,因为 then-and-只有这样容器才会在内部真正移动数据而不是复制。
今天尝试时,我发现 -std=c++1y(对于 C++14)和 g++-4.8 没有区别。也许我错过了规范中的更新,也许我的例子是错误的。
我比较了三个应该可以移动的数据结构的区别
- 默认情况下可根据“零规则”移动
- 通过提供移动操作可移动没有
noexcept - 通过提供移动操作with可移动
noexcept
框架:
#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;
}
}
使用我的三种数据类型:
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; }
};
并执行计时:
int main() {
timeStuff<RuleOfZeroVector>("RuleOfZeroVector");
timeStuff<MoveExceptVector>("MoveExceptVector");
timeStuff<MoveNoExceptVector>("MoveNoExceptVector");
}
有了结果:
RuleOfZeroVector... 2461 ms
MoveExceptVector... 2472 ms
MoveNoExceptVector... 2468 ms
如您所见,没有真正的区别。
我预计MoveExceptVector 会比其他两个慢得多,因为我假设vector 在内部数据结构增长时会使用大量复制。错了吗?
【问题讨论】:
-
向
MoveExceptVector添加一个复制ctor/assignment 操作符,你会看到区别。 -
vector::clear()不需要移动数据。它只是运行每个元素的析构函数。vector::push_back是noexcept搬家建设的主要受益人。 -
"g++-4.8 甚至。" 4.8 系列于 2013 年发布。那是很久以前的事了。
-
@towi :如果有相应的用户定义的移动特殊成员,则不隐式生成复制特殊成员。如果您刚刚完成
MoveExceptVector(MoveExceptVector const&) = default;并测试,您会发现差异很明显。 >_> -
@T.C. : 确实,但如果我觉得有必要给出详细的迂腐解释,我会回答而不是评论。 ;-]
标签: c++ c++14 move-semantics c++17 noexcept