返回复合类型时,`std::move` 对象进入构造函数?

Ado*_*uka 0 c++ move

我有一个班级,其中有几个(可能很大)std::vector<T>成员。据我所知,这不算是坏风格。简化示例:

// C++17
#include<vector>

struct ThreeVectors {
    std::vector<int> v1;
    std::vector<int> v2;
    std::vector<int> v3;
};
Run Code Online (Sandbox Code Playgroud)

然后我还有一个函数,它计算一些std::vector<int>参数并将它们包装成一个ThreeVectors对象:

ThreeVectors no_move(const std::vector<int> &vv1, // copy manually in the function
                     std::vector<int> vv2,  // copy when passing argument
                     const std::vector<int> &vv3 // copy when wrapping into ThreeVectors
) {
    auto modvv = vv1;  // make a copy
    // perform lots of manipulations, including changing size (e.g. filtering).
    modvv.at(0)++;

    vv2.at(0)++;  // perform lots of manipulations

    // do nothing with vv3.

    return ThreeVectors{modvv, vv2, vv3};
}
Run Code Online (Sandbox Code Playgroud)

示例用户代码:

void user() {
    std::vector<int> v1;
    std::vector<int> v2;
    std::vector<int> v3;
    for (int i = 0; i < 10'000'000; ++i) {
        v1.push_back(i % 10'000);
        v2.push_back(i % 11'000 + 30);
        v3.push_back(i % 12'000 + 60);
    }

    auto wrapped = no_move(v1, v2, v3);
    // no longer need v2. Could std::move it if that helps?
    // still need v1 and v3. Cannot avoid one copy but avoid more?
    // ...
}
Run Code Online (Sandbox Code Playgroud)

问题:我应该在任何地方使用移动操作来提高效率吗?

特别是,以下问题可能很有趣:

  1. user可调用no_move与向量的user不再需要。将它移到函数中是否有意义(这仅对函数不接受 by 的参数有意义const&)?
  2. 如果const&无论如何必须制作副本,该函数是否应该永远不接受 a ?
  3. 应该no_move将东西移动到 的构造函数中ThreeVectors吗?
  4. no_move不修改其最后一个参数而仅包装它的事实是否在应如何处理该参数方面发挥任何作用?

我尝试分析可以std::move使用 quick-bench.com放置的不同位置组合,但我不断收到运行时错误(这可能意味着我使用std::move不正确)。我没有用所有不同的组合向我的问题发送垃圾邮件,所有这些组合都可以编译但其中许多在运行时崩溃,我要求的是最佳实践解决方案。

HTN*_*TNW 5

你说得对,规则是,const&无论如何,当你要复制时,获取会适得其反。唯一能做的就是避免复制。按值取,从里面的值移动no_move,然后用户代码可以决定参数是否可以移动或是否必须复制。不,vv3被转发但被vv1操纵的事实并不意味着他们被区别对待。唯一重要的是该功能是“拥有”两者的所有权。

另外,请注意它ThreeVectors没有构造函数。它通过聚合初始化进行初始化,它像通过field(initializer);(复制初始化)一样初始化每个字段。所以你最好把std::moves 放在那里。

ThreeVectors no_move(std::vector<int> vv1, // copy OR move from user
                     std::vector<int> vv2, // copy OR move from user
                     std::vector<int> vv3  // copy OR move from user
) {
    vv1.at(0)++;
    vv2.at(0)++;
    return {std::move(vv1), std::move(vv2), std::move(vv3)};
}
Run Code Online (Sandbox Code Playgroud)

请注意,做对了比做错更简单

void user() {
    std::vector<int> v1, v2, v3;
    for (int i = 0; i < 10'000'000; ++i) {
        v1.push_back(i % 10'000);
        v2.push_back(i % 11'000 + 30);
        v3.push_back(i % 12'000 + 60);
    }
    auto wrapped = no_move(v1, std::move(v2), v3);
    // say what you mean:
    //  1. v1 and v3 must remain under user's ownership and no_move must
    //     receive copies, since it also wants ownership
    //  2. user no longer needs v2 and it can just be given to no_move
}
Run Code Online (Sandbox Code Playgroud)