【问题标题】:std::vector and Move constructor [duplicate]std::vector 和 Move 构造函数
【发布时间】:2013-12-08 11:31:32
【问题描述】:

我想使用 C++11 移动语义。我写了以下课程:

class ColorM
{
public:
    ColorM(float _r, float _g, float _b, float _a){
        qDebug()<<"Constructor";
        r = _r;
        g = _g;
        b = _b;
        a = _a;

        m = new float[16];
    }


    ColorM(const ColorM &other){
        qDebug()<<"Copy Constructor";
    }


    ~ColorM(){
        if (m != nullptr)
        {
           qDebug()<<"Deleting resource.";
           // Delete the resource.
           delete[] m;
        }
    }

    // Move constructor.
    ColorM(ColorM&& other)
    {
        qDebug()<<"Move Constructor";
       r = other.r;
       g = other.g;
       b = other.b;
       a = other.a;
       m = other.m;


       other.m = nullptr;
    }


    float r;
    float g;
    float b;
    float a;

    float *m;
private:
};

当我尝试:

std::vector<ColorM> vec;
vec.push_back(ColorM(0.1, 0.6, 0.3, 0.7));
vec.push_back(ColorM(0.2, 0.6, 0.3, 0.7));
vec.push_back(ColorM(0.3, 0.6, 0.3, 0.7));

我收到了复制构造函数调用。我做错了什么?
我以this 为例。并用g++编译。

这是我用于测试的 QT 项目:http://wikisend.com/download/261514/MoveConstructor.zip

【问题讨论】:

标签: c++11 stl move-semantics rvalue-reference


【解决方案1】:

你可以使用std::vector&lt;T&gt;::emplace_back函数

std::vector<ColorM> vec;
vec.emplace_back(0.1, 0.6, 0.3, 0.7);  // Will call 
vec.emplace_back(0.2, 0.6, 0.3, 0.7);  //     ColorM::ColorM(float _r, float _g, ..) ctor
vec.emplace_back(0.3, 0.6, 0.3, 0.7);

或者(使用ColorM移动构造函数)

std::vector<ColorM> vec;
vec.emplace_back(ColorM(0.1, 0.6, 0.3, 0.7));  // Should be equivalent to your code
vec.emplace_back(ColorM(0.2, 0.6, 0.3, 0.7));
vec.emplace_back(ColorM(0.3, 0.6, 0.3, 0.7));

注意:我不同意user2485710 的回答。由于ColorM(0.1, 0.6, 0.3, 0.7)push_back 参数是临时的,我希望编译器将其识别为r 值引用并应用移动构造函数,不使用 使用std::move,因为push_back has an overload for r-value references.

换句话说,采用ColorM 对象的emplace_back 应该避免像push_back 这样的副本;在emplace_back 的情况下,您必须就地构建。

使用上面的第二个版本,与push_back 相比,您应该获得更好的诊断消息,即编译时失败,而不是这种微妙的意外使用复制构造函数。

【讨论】:

  • "也就是说emplace_back应该和push_back表现一样"差不多; emplace_back 甚至不需要移动,它只是在原地构造值。
  • 谢谢,DyP,我会编辑澄清。我指的是vec.emplace_back(ColorM(0.1, 0.6, 0.3, 0.7));,它将使用移动构造函数ColorM(ColorM&amp;&amp; other)
  • emplace_back 使用std::forward std::move 来构造对象,这与我说的不矛盾,这2只是2个转换,唯一的区别是1 是无条件的,而另一个确实是有条件的,emplace_back 在内部完成所有这些。不要忘记一件事:什么可以是右值或纯右值或其他什么,是 expression not 对象,这意味着在判断什么之前您可能需要语义是什么。
【解决方案2】:

这是因为你的构造函数的签名和给你push_back()操作的参数。

简而言之T() 生成T 类型对象的新实例,如果此实例与标签/变量无关,则该实例保持unnamed,但不会更改其自身类型,它总是T 类型的“东西”(它甚至可以被认为是T&amp;const T&amp;,但这不是本主题的论点)。

要获得T&amp;&amp; 签名,您需要从castT&amp;&amp;,而在C++11 中,这是std::move 的工作,这也是一种无条件类型转换,因此非常易于使用几乎在任何地方。

您可以将std::move 添加到您的类内或push_back 内的构造函数中,您的调用。

【讨论】:

  • 请看这里:msdn.microsoft.com/en-us/library/vstudio/dd293665.aspx。 msdn 说 v.push_back(MemoryBlock(25)); 就够了
  • @tower120 他们只是选择使用std::move cast inside移动构造函数。
  • 根本没有关于异常的说法!但我想你是对的。
  • @tower120 重点是std::moveT&amp;&amp; 的转换,一旦你熟悉了它,这只是一个设计问题,你认为在哪里更适合使用和实现它转换。
  • 也许吧,但我认为 MSDN 文章会教我:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-03
  • 2015-02-14
  • 2014-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多