简体   繁体   English

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

[英]C++11 - I lost a move/copy assignment

I posted this code in another question but I have a new doubt on it: 我在另一个问题上发布了这个代码,但我对此有了新的疑问:

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

In the main() lines I would expect the move assignment and the copy assignment to get called... but they don't! 在main()行中,我希望调用移动赋值和复制赋值......但它们不会!

MSVC2012 output is: MSVC2012输出是:

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

And g++'s one is 而g ++就是

X default constructor called X default constructor called X默认构造函数调用X默认构造函数调用

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

Where are the assignments?? 作业在哪里? I thought the first main() line would call a move assignment and the second main() line would call a copy assignment 我认为第一个main()行将调用移动赋值,第二个main()行将调用复制赋值

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

First of all, these are not the copy constructor and copy assignment operator for X2 because they take arguments of type X . 首先,这些不是X2的复制构造函数和复制赋值运算符,因为它们接受X类型的参数。 The first is actually known as a converting constructor, because it can convert from an X to an X2 . 第一种实际上称为转换构造函数,因为它可以从X转换为X2

int x = 5;

This is not 5 being assigned to x ; 这不是5分配给x ; it is x being initialised with 5 . 它是x与正被初始化5 Initialisation, although it looks similar, is not the same as assignment. 初始化虽然看起来很相似,但与赋值不同。 In fact, no assignment occurs at all in your code, so the move or copy assignment operators will not be used. 实际上,代码中根本不会发生任何赋值,因此不会使用移动或复制赋值运算符。

We can take a look at what each of the compilers you've given is actually doing: 我们可以看看你给出的每个编译器实际上在做什么:

  1. MSVC MSVC

    First, myNewObject is created in make_x . 首先, myNewObject在创建make_x This prints out X default constructor called . 这打印出X default constructor called Then the return myNewObject; 然后return myNewObject; will treat the copy to the return value as a move first, find that there is a move constructor, and invoke it. 将复制视为返回值作为移动优先,发现有一个移动构造函数,并调用它。

    When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. 当满足或将满足复制操作的省略标准时,除了源对象是函数参数这一事实,并且要复制的对象由左值指定,重载决策选择复制的构造函数是首先执行,好像对象是由右值指定的。

    The return value would then be copied into x1 . 然后将返回值复制到x1 However, this copy has clearly been elided because we see no X copy constructor called output: 但是,这个副本显然已被删除,因为我们看不到X copy constructor called output的X copy constructor called

    when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move 当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-nonqualified类型的类对象时,可以通过将临时对象直接构造到目标中来省略复制/移动操作省略的复制/移动

    Secondly, another myNewObject is created in the second call to make_x . 其次,另一个myNewObject在第二次调用创建make_x This again prints out X default constructor called . 这再次打印出X default constructor called Then the same move happens when doing return myNewObject . 然后在return myNewObject时发生相同的移动。 The construction of x2 from the return value doesn't output anything because its constructor that takes an X doesn't do any output. 从返回值构造x2不会输出任何内容,因为其带有X构造函数不会执行任何输出。

  2. GCC GCC

    First, myNewObject is created in make_x , just as with MSVC. 首先, myNewObject在创建make_x ,就像MSVC。 This prints out X default constructor called . 这打印出X default constructor called

    Now GCC does an extra optimization that MSVC isn't doing. 现在,GCC进行了MSVC没有做的额外优化。 It realises that it might as well not bother moving from myNewObject to the return value and instead just construct it directly in the return value's place: 它意识到它可能不会费心从myNewObject移动到返回值,而只是直接在返回值的位置构造它:

    in a return statement in a function with a class return type, when the expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) with the same cvunqualified type as the function return type, the copy/move operation can be omitted by constructing the automatic object directly into the function's return value 在具有类返回类型的函数的return语句中,当表达式是具有与函数返回类型相同的cvunqualified类型的非易失性自动对象(函数或catch子句参数除外)的名称时,副本通过将自动对象直接构造到函数的返回值中,可以省略/ move操作

    Then the same elision caused by constructing x1 from a temporary object is performed as was in MSVC. 然后,如在MSVC中那样执行从临时对象构造x1导致的相同省略。

    The second call to make_x occurs in exactly the same way as with the first, except now x2 is constructed by the converting constructor that takes an X . make_x的第二次调用以与第一次调用完全相同的方式发生,除了现在x2由采用X的转换构造函数构造。 This, of course, doesn't output anything. 当然,这不会产生任何结果。

You are seing the effect of the named return value optimization , NRVO. 您正在查看命名返回值优化 NRVO的效果。 The following code 以下代码

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

can remove the temporary completely and construct it in the function caller's return slot. 可以完全删除临时并在函数调用者的返回槽中构造它。 In your example the effect is like the function is inlined and constructs x1 and x2 directly. 在您的示例中,效果就像函数内联并直接构造x1x2

If you want the assignment to be seen, you can write: 如果你想看到作业,你可以写:

X x1;
x1 = make_x();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM