【问题标题】:Vector building optimization using std::move使用 std::move 优化向量构建
【发布时间】:2020-11-15 11:56:14
【问题描述】:

我正在构建一些std::vector<Obj>,其中Obj 是可以移动构造和分配的大型对象(例如,考虑Obj 是大向量)。代码通常是一个循环,例如

std::vector<Obj> v;
while (...) {
    Obj foo = some client code(...);
    // ... some complicated stuff modifying foo
    v.push_back(foo);
}

如您所见,foo 被推入向量后就不需要了。

我的问题是

  • 写起来有意义吗

      v.push_back(std::move(foo));
    

    向编译器表明它可以获取 foo 的内容。

  • 如果有,真的需要吗?事实上,编译器可能会注意到foo 在被推送后立即被破坏,以便可以移动它......实际的编译器是否使用这些优化?

【问题讨论】:

    标签: c++ c++11 optimization move


    【解决方案1】:

    除非在非常特殊的情况下,否则编译器不允许调用移动构造函数或删除对象而不显式移动(即强制转换为右值引用;可能通过 std 移动)。

    A) 该对象是一个临时的未命名对象。

    B) 对象正在从函数中返回。

    还有一些其他要求,但以上两个都不适用。所以不允许转换为移动/省略。

    编译器有时可以使用 as-if 规则将副本转换为移动,然后将其销毁,但这非常困难且通常很脆弱,因此您永远不应该依赖它。

    【讨论】:

    • 所以我一定要加招!感谢您的澄清!
    【解决方案2】:

    编译器是否可以优化它取决于 Obj 的外观,在执行“修改 foo 的复杂内容”时调用的函数,Obj 是否具有移动构造函数,等等。您应该尝试编译和基准测试代码并亲自查看,或使用https://godbolt.org/ 等在线服务查看生成的汇编代码。

    但是,最好的方法是完全避免复制或移动。您可以通过调用emplace_back() 直接在向量中构造一个Obj 来做到这一点,然后就地修改它,如下所示:

    while (...) {
        v.emplace_back();
        Obj &foo = v.back();
        // ... some complicated stuff modifying foo
    }
    

    你可以用 C++17 写得稍微短一些:

    while (...) {
        Obj &foo = v.emplace_back();
        // ... some complicated stuff modifying foo
    }
    

    【讨论】:

    • 感谢您的回答!两个 cmets:你没有回答第一个问题:自己做 std::move 有用吗?特别是,在我的例子中, foo 是由一些客户端代码构造的(参见我的编辑),所以我无法决定它是在哪里构造的。抱歉,如果我的问题不够具体。
    • 回答我自己的评论...我只是意识到我可以做到v.emplace_back(some client code()); 所以我评论的第二部分没有多大意义。我仍然对第一个问题的答案很感兴趣。
    • @hivert 如果不了解Obj 的更多信息,我真的无法回答第一个问题。例如,如果Obj 只是一个int,那么std::move() 当然不会有任何效果。如果它是带有移动构造函数的东西,那么它可能是,因为 push_back() 确实有 r 值引用的重载。
    猜你喜欢
    • 1970-01-01
    • 2022-10-01
    • 2019-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-21
    • 1970-01-01
    • 2013-10-16
    相关资源
    最近更新 更多