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

时间:2019-05-03 07:49:52

标签: c++ c++11 move-semantics

下面的代码将包含大向量的对象传递到向量中。我希望它表现出色。我是否需要在对test的调用中将push_back强制转换为右值?我需要告诉编译器如何移动结构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;
}

2 个答案:

答案 0 :(得分:4)

  

我希望它表现出色

以下内容应该足够好。我希望这些注释可以帮助您理解代码。

#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;
}

答案 1 :(得分:2)

您的Test结构未定义任何特殊的成员函数(复制构造函数,析构函数等),这意味着将自动生成默认的移动赋值运算符和默认的移动副本构造函数,它们将移动每个数据成员的结构。因此Test是可移动类型,并且由于vector<size_t>是可移动数据成员而从中受益。

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

    vecOfTest.push_back(test);
}

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

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

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

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

会采取行动,但最好不要这样做。最好:

return test;

相反。这不是一个隐含的举动。这是一个省略。清除比移动快,而移动将阻止清除。但是,在无法进行省略的情况下,将执行隐式移动。这是我唯一知道发生隐式移动的地方:作为省略的替代。您的原始代码:

vecOfTest.push_back(test);

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