And*_*owl 8 c++ copy-constructor move-semantics c++11
给定X下面的类(除了明确定义的特殊成员函数与此实验无关):
struct X
{
X() { }
X(int) { }
X(X const&) { std::cout << "X(X const&)" << std::endl; }
X(X&&) { std::cout << "X(X&&)" << std::endl; }
};
Run Code Online (Sandbox Code Playgroud)
以下程序创建一个类型对象的向量X并调整其大小,以便超出其容量并强制重新分配:
#include <iostream>
#include <vector>
int main()
{
std::vector<X> v(5);
v.resize(v.capacity() + 1);
}
Run Code Online (Sandbox Code Playgroud)
由于class X提供了一个移动构造函数,我希望在重新分配之后将向量的先前内容移动到新存储中.相当令人惊讶的是,情况似乎并非如此,我得到的输出是:
X(X const&)
X(X const&)
X(X const&)
X(X const&)
X(X const&)
Run Code Online (Sandbox Code Playgroud)
为什么?
And*_*owl 17
C++ 11标准的第23.3.6.3/14段规定(关于类模板的resize()成员函数vector<>):
备注:如果抛出异常而非非移动构造函数,
CopyInsertable T则无效.
换句话说,这意味着对于X(即CopyInsertable),resize()提供了强有力的保证:它要么成功,要么保持向量的状态不变.
为了满足这种保证,实现通常采用复制和交换习惯用法:如果X抛出的复制构造函数,我们还没有改变原始向量的内容,所以保留了承诺.
但是,如果向量的先前内容被移动到新存储中而不是被复制并且移动构造函数抛出,那么我们将不可逆转地改变向量的原始内容.
因此,实现将使用复制构造函数X将向量的内容安全地传输到新存储中,除非已知移动构造函数不抛出,在这种情况下,从前面的元素移动是安全的.
事实上,对于X移动构造函数(将其标记为noexcept)的定义稍作更改,程序的输出现在是预期的.
struct X
{
X() { }
X(int) { }
X(X const&) { std::cout << "X(X const&)" << std::endl; }
X(X&&) noexcept { std::cout << "X(X&&)" << std::endl; }
// ^^^^^^^^
};
Run Code Online (Sandbox Code Playgroud)
考虑异常保证:如果在重新分配期间出现异常,则向量必须保持不变.这只能通过复制元素并保留旧集来保证,直到整个副本成功为止.
只有当您知道移动构造函数不抛出时,才能安全地将元素移动到新位置.为此,请声明移动构造函数noexcept.
| 归档时间: |
|
| 查看次数: |
2124 次 |
| 最近记录: |