将自定义对象添加到矢量时如何启用移动语义?

How to enable move semantics when adding custom objects to a vector?

下面的代码将包含大向量的对象传递到向量中。我希望这是高性能的。我是否需要在调用 push_back 时将 test 转换为右值?我需要告诉编译器如何移动 struct Test 的实例吗?还是这一切都会自动进行?

int main()
{
    struct Test
    {
        std::vector<size_t> vals;
        double sum;
    };
    std::vector<Test> vecOfTest;
    vecOfTest.reserve(100000);

    for (size_t i = 0; i < 100000; i++)
    {
        Test test{};
        test.vals.reserve(i);
        for (size_t j = 0; j < i; j++)
        {
            test.vals.push_back(j);
            test.sum += j;
        }
        vecOfTest.push_back(test);
    }


    return 0;
}

I want this to be performant

以下应该足够好了。希望评论对大家理解代码有帮助。

#include <vector>
#include <iostream>
#include <numeric>

struct Test
{
    std::vector<size_t> vals;
    double sum = 0; // initialing is a good idea
    Test(const size_t v, const double res) // provide constructor(appropriate one)
        : vals(v), // tell the size of the vals directly in the constructor
          sum(res) 
    {}
};

int main()
{

    std::vector<Test> vecOfTest;
    vecOfTest.reserve(100000);

    for (size_t i = 0; i < 100000; i++)
    {
        // Test& last_test = vecOfTest.emplace_back() needs C++17, otherwise
        // use std::vector::back()
        auto& last_test = vecOfTest.emplace_back(   // create the Test object in place and take the reference to it
            i,                     // tell the size of vals in newly creating Test object
            ((i - 1) * i) / 2.0    // ((j-1) * j)/2 = sum from 0 to j-1
        );
        std::iota(std::begin(last_test.vals), std::end(last_test.vals), static_cast<size_t>(0)); // populate, 0 to size of vals
    }
    return 0;
}

你的Test结构没有定义任何特殊的成员函数(复制构造函数、析构函数等),这意味着默认的移动赋值运算符和默认的移动复制构造函数是自动生成的,它们将分别移动结构的数据成员。所以 Test 是一个可移动类型,它从中受益,因为 vector<size_t> 是一个可移动数据成员。

但是,移动不会自动执行,因为从一个对象移动会改变它。即使您认为这:

    vecOfTest.push_back(test);
}

会做一个隐含的移动,因为范围结束,它不会。隐式移动会使编译器和程序员都陷入困境。编译器需要证明使 test 无效是可以的。程序员将需要不断调查是否需要显式移动,而最终结果将是无论如何都要进行显式移动。因此,由于这个原因,隐式移动不会发生(但请参阅下面的规则例外情况。)你需要自己做:

vecOfTest.push_back(std::move(test));

唯一不需要移动的情况是移动会干扰省略。例如,在 returns a Test 的函数中,这个:

Test test;
return std::move(test);

会移动,但最好不要移动。最好是:

return test;

相反。这不是隐含的举动。这是一个省略。省略比移动快,并且移动可以防止省略。但是,在不可能进行省略的情况下,将执行隐式移动。这是我所知道的唯一会发生隐式移动的情况:作为省略的替代品。您的原始代码:

vecOfTest.push_back(test);

不是省略的情况,因此永远不会发生隐式移动。