tow*_*owi 6 c++ move-semantics noexcept c++14 c++17
在阅读关于C++ 11时,我感觉当使用标准容器(如std::vector
)和用户定义的数据类型时,鼓励人们提供noexcept
移动操作,如果有的话,因为那时候容器会在内部真正移动数据而不是复制.
今天尝试时,我发现-std=c++1y
(对于C++ 14)和g ++ - 4.8都没有区别.也许我错过了规范中的更新,也许我的例子是错误的.
我比较了三个应该可以移动的数据结构
noexcept
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;
}
}
Run Code Online (Sandbox Code Playgroud)
使用我的三种数据类型:
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; }
};
Run Code Online (Sandbox Code Playgroud)
并执行时间:
int main() {
timeStuff<RuleOfZeroVector>("RuleOfZeroVector");
timeStuff<MoveExceptVector>("MoveExceptVector");
timeStuff<MoveNoExceptVector>("MoveNoExceptVector");
}
Run Code Online (Sandbox Code Playgroud)
结果如下:
RuleOfZeroVector... 2461 ms
MoveExceptVector... 2472 ms
MoveNoExceptVector... 2468 ms
Run Code Online (Sandbox Code Playgroud)
如你所见,没有真正的区别.
我预计MoveExceptVector
会比其他两个慢得多,因为我假设vector
当内部数据结构增长时会使用大量复制.错误?
vector
用于在末尾插入单个元素的相关规则是:
noexcept
),则移动。在这种情况下,在向量末尾插入提供了强大的异常安全保证。在您的示例中,对象不可复制,因为用户提供的移动构造函数和移动赋值运算符会导致隐式复制构造函数被定义为已删除。所以vector
别无选择。它必须使用移动操作,即使它们不是noexcept
。
如果您使类型可复制,您应该会看到差异,速度 MoveExceptVector
要慢得多,因为现在vector
可以在可能抛出的移动或复制之间进行选择,因此它选择复制。
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; }
// ADDED:
MoveExceptVector(const MoveExceptVector&) = default;
};
Run Code Online (Sandbox Code Playgroud)
如果对象不可复制,您就不能指望它选择复制。