4

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

#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 移动构造函数调用 X 默认构造函数调用 X 移动构造函数调用

而g ++的一个是

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

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

任务在哪里??我以为第一行 main() 会调用移动赋值,第二行 main() 会调用复制赋值

4

2 回答 2

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

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

int x = 5;

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

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

  1. MSVC

    首先,myNewObject创建于make_x. 这打印出来X default constructor called。然后return myNewObject;会先把复制到返回值当做move,发现有move构造函数,调用它。

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

    然后将返回值复制到x1. 然而,这个副本显然被忽略了,因为我们没有看到X copy constructor called输出:

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

    其次,myNewObject在第二次调用make_x. 这再次打印出来X default constructor called。然后在做的时候也会发生同样的动作return myNewObject。从返回值构造 ofx2不输出任何东西,因为它的构造函数接受 anX不做任何输出。

  2. 海合会

    首先,myNewObject是在 中创建的make_x,就像 MSVC 一样。这打印出来X default constructor called

    现在 GCC 做了一个 MSVC 没有做的额外优化。它意识到它最好不要费心从myNewObject返回值移动,而是直接在返回值的位置构造它:

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

    x1然后像在 MSVC 中一样执行由临时对象构造引起的相同省略。

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

于 2013-04-02T18:21:43.600 回答
5

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

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

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

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

X x1;
x1 = make_x();
于 2013-04-02T18:20:22.690 回答