代码之家  ›  专栏  ›  技术社区  ›  Andy Prowl

当容量超过时,为什么resize()会导致向量内容的复制,而不是移动?[副本]

  •  11
  • Andy Prowl  · 技术社区  · 12 年前

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

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

    以下程序创建类型为的对象的矢量 十、 并调整其大小,以使其容量被超过并强制重新分配:

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

    自上课以来 十、 提供了一个move构造函数,我希望向量的前一个内容是 感动的 重新分配后放入新的存储器。令人惊讶的是, that does not seem to be the case ,得到的输出是:

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

    为什么?

    2 回复  |  直到 12 年前
        1
  •  21
  •   Community CDub    8 年前

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

    评论 :如果异常不是由非的move构造函数引发的- CopyInsertable T 没有任何影响 .

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

    为了满足这一保证,实现通常采用 copy-and-swap idiom :如果的复制构造函数 十、 throws,我们还没有改变原始向量的内容,所以承诺得到了遵守。

    但是,如果矢量的先前内容 感动的 而不是被复制到新的存储器中 move构造函数抛出 ,那么我们就会不可逆转地改变向量的原始内容。

    因此,实现将使用的复制构造函数 十、 将矢量的内容安全地转移到新的存储器中 除非知道move构造函数不抛出 ,在这种情况下,可以安全地从先前的元素中移出。

    十、 的move构造函数(将其标记为 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; }
    //         ^^^^^^^^
    };
    
        2
  •  5
  •   Kerrek SB    12 年前

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

    只有知道move构造函数不会抛出,才能安全地将元素移动到新位置。要实现这一点,请声明move构造函数 noexcept .