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

Joh*_*ing 4 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
}
Run Code Online (Sandbox Code Playgroud)

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

MSVC2012输出是:

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

而g ++就是

X默认构造函数调用X默认构造函数调用

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

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

Jos*_*eld 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;
}
Run Code Online (Sandbox Code Playgroud)

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

int x = 5;
Run Code Online (Sandbox Code Playgroud)

这不是5分配给x; 它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

    首先,与MSVC一样,myNewObject创建make_x.打印出来X default constructor called.

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

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

    然后,x1如在MSVC中那样执行从临时对象构造所引起的相同省略.

    第二次调用以make_x与第一次调用完全相同的方式发生,除了现在x2由转换构造函数构造,它采用了X.当然,这不会产生任何结果.

  • @JohnnyPauling完成.移动是从'myNewObject`到返回值. (2认同)
  • +1 [来自X2成员的输出](http://ideone.com/Gj2qee)可能会为正在发生的事情带来更多的清晰度.我同意本说明书中的评估. (2认同)

Dan*_*rey 5

您正在查看命名返回值优化 NRVO的效果.以下代码

X f()
{
  X tmp;
  // ...
  return tmp;
}
Run Code Online (Sandbox Code Playgroud)

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

如果你想看到作业,你可以写:

X x1;
x1 = make_x();
Run Code Online (Sandbox Code Playgroud)