C ++ 11 - 我丢失了移动/复制任务

时间:2013-04-02 18:17:18

标签: c++ c++11

我在另一个问题上发布了此代码,但我对它有了新的疑问:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class X
{
    public:

    std::vector<double> data;

    // Constructor1
    X():
        data(100000) // lots of data
    {
        cout << "X default constructor called";
    }

    // Constructor2
    X(X const& other): // copy constructor
        data(other.data)   // duplicate all that data
    {
        cout << "X copy constructor called";
    }

    // Constructor3
    X(X&& other):  // move constructor
        data(std::move(other.data)) // move the data: no copies
    {
        cout << "X move constructor called";
    }

    X& operator=(X const& other) // copy-assignment
    {
        cout << "X copy assignment called";
        data=other.data; // copy all the data
        return *this;
    }

    X& operator=(X && other) // move-assignment
    {
        cout << "X move assignment called";
        data=std::move(other.data); // move the data: no copies
        return *this;
    }

};

class X2
{
    public:

    std::vector<double> data;

    // Constructor1
    X2():
        data(100000) // lots of data
    {}

    // Constructor2
    X2(X const& other): // copy constructor
        data(other.data)   // duplicate all that data
    {}

    X2& operator=(X const& other) // copy-assignment
    {
        data=other.data; // copy all the data
        return *this;
    }
};

X make_x()
{
    X myNewObject; // Il normale costruttore viene chiamato qui
    myNewObject.data.push_back(22);
    return myNewObject; // Si crea un oggetto temporaneo prima di ritornare con il move constructor perchè myNewObject dev'essere distrutto
}


int main()
{
    X x1 = make_x(); // x1 has a move constructor


    X2 x2 = make_x(); // x2 hasn't a move constructor
}

在main()行中,我希望调用移动赋值和复制赋值......但它们不会!

MSVC2012输出为:

  

X默认构造函数名为X move constructor,名为X default   名为X的构造函数移动构造函数,名为

g ++的一个是

  

X默认构造函数,名为X默认构造函数,名为

http://liveworkspace.org/code/220erd $ 2

作业在哪里?我认为第一个main()行将调用移动赋值,第二个main()行将调用复制赋值

2 个答案:

答案 0 :(得分:8)

// Constructor2
X2(X const& other): // copy constructor
    data(other.data)   // duplicate all that data
{}

X2& operator=(X const& other) // copy-assignment
{
    data=other.data; // copy all the data
    return *this;
}

首先,这些不是X2的复制构造函数和复制赋值运算符,因为它们采用类型X的参数。第一个实际上称为转换构造函数,因为它可以从X转换为X2

int x = 5;

这不是分配给x的5;使用x初始化5。初始化虽然看起来很相似,但与赋值不同。实际上,代码中根本不会发生任何赋值,因此不会使用移动或复制赋值运算符。

我们可以看看你给出的每个编译器实际上在做什么:

  1. MSVC

    首先,在myNewObject中创建make_x。这打印出X default constructor called。然后return myNewObject;将复制视为返回值作为移动,发现有一个移动构造函数,并调用它。

      

    当满足或将满足复制操作的省略标准时,除了源对象是函数参数,并且要复制的对象由左值指定,重载决策以选择构造函数首先执行复制,就像对象是由右值指定一样。

    然后将返回值复制到x1。但是,这个副本显然已被删除,因为我们看不到X copy constructor called输出:

      

    当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-nonqualified类型的类对象时,可以通过直接构造临时对象来省略复制/移动操作进入省略的复制/移动目标

    其次,在myNewObject的第二次调用中创建了另一个make_x。这再次打印出X default constructor called。然后在执行return myNewObject时发生同样的动作。从返回值构造x2不会输出任何内容,因为它带有X的构造函数不会输出任何内容。

  2. GCC

    首先,在myNewObject中创建make_x,与MSVC一样。这打印出X default constructor called

    现在GCC进行了MSVC没有做的额外优化。它意识到它可能不会费心从myNewObject转移到返回值,而只是直接在返回值的位置构建它:

      

    在具有类返回类型的函数的return语句中,当表达式是具有与函数返回类型相同的cvunqualified类型的非易失性自动对象(函数或catch子句参数除外)的名称时,通过将自动对象直接构造到函数的返回值

    中,可以省略复制/移动操作

    然后,在MSVC中执行从临时对象构造x1所导致的相同省略。

    make_x的第二次调用与第一次调用的方式完全相同,除了现在x2由转换构造函数构造X。当然,这不会输出任何内容。

答案 1 :(得分:5)

你正在看到named return value optimization,NRVO的效果。以下代码

X f()
{
  X tmp;
  // ...
  return tmp;
}

可以完全删除临时值并在函数调用者的返回槽中构造它。在您的示例中,效果就像函数内联并直接构造x1x2

如果您希望看到作业,可以写下:

X x1;
x1 = make_x();