为什么要重新分配向量副本而不是移动元素? [复制]

2024-05-02

可能的重复:
当向量增长时如何强制移动语义? https://stackoverflow.com/questions/8001823/how-to-enforce-move-semantics-when-a-vector-grows

insert, push_back and emplace(_back)可能会导致重新分配std::vector。我很困惑地看到下面的代码copies的元素而不是moving他们在重新分配容器时。

#include <iostream>
#include <vector>

struct foo {
    int value;

    explicit foo(int value) : value(value) {
        std::cout << "foo(" << value << ")\n";
    }

    foo(foo const& other) noexcept : value(other.value) {
        std::cout << "foo(foo(" << value << "))\n";
    }

    foo(foo&& other) noexcept : value(std::move(other.value)) {
        other.value = -1;
        std::cout << "foo(move(foo(" << value << "))\n";
    }

    ~foo() {
        if (value != -1)
            std::cout << "~foo(" << value << ")\n";
    }
};

int main() {
    std::vector<foo> foos;
    foos.emplace_back(1);
    foos.emplace_back(2);
}

在我的特定机器上使用特定编译器(GCC 4.7)打印以下内容:

foo(1)
foo(2)
foo(foo(1))
~foo(1)
~foo(1)
~foo(2)

但是,当删除复制构造函数时(foo(foo const&) = delete;),生成以下(预期)输出:

foo(1)
foo(2)
foo(move(foo(1))
~foo(1)
~foo(2)

这是为什么?移动通常不会比复制更有效,或者至少不会比复制低很多吗?

值得注意的是GCC 4.5.1 做了预期的事情 http://ideone.com/2EPxu– 这是 GCC 4.7 中的回归,还是一些巧妙的优化,因为编译器发现我的对象复制起来很便宜(但如何复制?!)?

另请注意,我确保这is由重新分配引起,通过实验性地放置foos.reserve(2);在插入的前面;这不会导致复制或移动被执行。


简短的回答是我认为@BenVoigt 基本上是正确的。

在描述中reserve(§23.3.6.3/2),它说:

如果异常是由非 CopyInsertable 类型的移动构造函数引发的,则不会有任何影响。

[以及描述resize§23.3.6.3/12 中也有同样的要求。]

这意味着如果 T 是 CopyInsertable,您将获得强大的异常安全性。为了保证这一点,它可以only使用移动构造如果it推断(通过未指定的方式)移动构造永远不会抛出。也不能保证throw() or noexcept不过,这将是必要或充分的。如果 T 是 CopyInsertable,则can只需选择始终使用复制构造即可。基本上,正在发生的事情是该标准需要类似复制构造的语义;编译器只能在 as-if 规则下使用移动构造,并且可以自由定义何时或是否执行该选项。

如果 T 不是 CopyInsertable,则重新分配will使用移动构造,但异常安全性取决于T的移动构造函数是否可以抛出。如果它不抛出,您将获得强大的异常安全性,但如果它抛出,您就不会(我认为您probably得到基本的保证,但也许连这个都没有,而且绝对没有更多)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么要重新分配向量副本而不是移动元素? [复制] 的相关文章

随机推荐