【问题标题】:Why does resize() cause a copy, rather than a move, of a vector's content when capacity is exceeded? [duplicate]当超出容量时,为什么 resize() 会导致向量内容的复制而不是移动? [复制]
【发布时间】:2013-03-21 19:01:07
【问题描述】:

给定类X 下面(明确定义的以外的特殊成员函数与本实验无关):

struct X
{
    X() { }
    X(int) { }
    X(X const&) { std::cout << "X(X const&)" << std::endl; }
    X(X&&) { std::cout << "X(X&&)" << std::endl; }
};

以下程序创建X 类型的对象向量并调整其大小,使其超出容量并强制重新分配:

#include <iostream>
#include <vector>

int main()
{
    std::vector<X> v(5);
    v.resize(v.capacity() + 1);
}

由于X 类提供了移动构造函数,我希望向量的先前内容在重新分配后移动 到新存储中。令人惊讶的是,that does not seem to be the case,我得到的输出是:

X(X const&)
X(X const&)
X(X const&)
X(X const&)
X(X const&)

为什么?

【问题讨论】:

  • 另请注意,Microsoft 不遵守此规则,因此此代码使用 Visual C++(或 clang Windows) 将调用移动构造函数,因此在调整大小失败的情况下,std::vector 中的元素可能会损坏(空)。

标签: c++ c++11 copy-constructor move-semantics


【解决方案1】:

C++11标准第23.3.6.3/14段规定(关于vector&lt;&gt;类模板的resize()成员函数):

备注:如果非CopyInsertable T的移动构造函数抛出异常没有影响

换句话说,这意味着对于X(即CopyInsertable),resize() 提供strong guarantee:它要么成功要么保持向量状态不变。

为了满足这个保证,实现通常采用copy-and-swap idiom:如果X的复制构造函数抛出,我们还没有改变原始向量的内容,所以保持承诺。

但是,如果向量的先前内容被移动到新存储中而不是被复制并且移动构造函数抛出,那么我们将不可逆转地更改原始内容向量的内容。

因此,实现将使用 X 的复制构造函数将向量的内容安全地传输到新存储中除非知道移动构造函数不会抛出,在这种情况下它是安全的从前面的元素移动。

X的移动构造函数的定义稍作改动(标记为noexcept),其实就是the output of the program is now the expected one。:

struct X
{
    X() { }
    X(int) { }
    X(X const&) { std::cout << "X(X const&)" << std::endl; }
    X(X&&) noexcept { std::cout << "X(X&&)" << std::endl; }
//         ^^^^^^^^
};

【讨论】:

  • 嗯,他们是,但通常只有当你有一个真正的问题,然后自己解决。如果您已经提前知道答案,那就有点可疑了。如果您只是想分享一些信息,我想您可以将问题标记为“FAQ”和“Community Wiki”。
  • @KerrekSB:但这就是发生的事情。我有一个问题,我弄清楚发生了什么,我想我会分享它。例如,参见this。我觉得还可以。
  • 回答您自己的问题,无论答案是否已知,都是explicitly encouraged
  • +1 请继续回答您自己的问题。我从你的回答中学到了很多。
  • @OlafDietsche:好的,谢谢您的支持。我很高兴有人发现它有帮助。
【解决方案2】:

考虑异常保证:如果在重新分配期间出现异常,向量必须保持不变。这只能通过复制元素并保留旧集直到整个复制成功来保证。

只有知道移动构造函数不会抛出异常,才能安全地将元素移动到新位置。为此,请声明移动构造函数noexcept

【讨论】:

    猜你喜欢
    • 2019-01-05
    • 2020-10-18
    • 1970-01-01
    • 2017-01-21
    • 1970-01-01
    • 2017-02-24
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    相关资源
    最近更新 更多