c ++ 11移动构造函数,使用右值通过未知方法初始化

c++11 move constructor, initializing by unknown method using rvalue

我做这个 TestClass 是为了更好地说明问题。 如您所见,当对象被推入向量时以及使用 std::move 函数初始化构造函数时,将调用移动构造函数。 但是当我们调用 TestClass testvar2(rvalue_func()); 你可以看到最后, rvalue_func() 的结果值被分配给 testvar2 对象,但是没有调用移动构造函数,也没有其他构造函数或赋值运算符被称为...

问题: 它是如何在不调用任何东西的情况下将值从 rvalue_func() 复制到 testvar2 的?为什么不调用移动构造函数?

#include <iostream>
using std::cout;
using std::endl;

#include <vector>

class TestClass {
public:

    TestClass(int arg_x=0, int arg_y=0) :
            values { nullptr } {

        values = new int[2];
        values[0] = arg_x;
        values[1] = arg_y;
        cout << "default constructor " << "x = " << values[0] << " y = "
                << values[1] << endl;
    }

    TestClass(const TestClass &arg) :
            values { nullptr } {

        values = new int[2];

        values[0] = arg.values[0];
        values[1] = arg.values[1];

        cout << "copy constructor " << "x = " << values[0] << " y = "
                << values[1] << endl;
    }

    TestClass(TestClass &&arg) :
            values { arg.values } {
        arg.values = nullptr;
        cout << "move constructor " << "x = " << values[0] << " y = "
                << values[1] << endl;
    }

    TestClass &operator=(TestClass &right) {
        cout << "assignment operator =" << endl;
        if (this != &right) {
            delete values;
            values = nullptr;
            values = new int[2];
            values[0] = right.values[0];
            values[1] = right.values[2];
        }
        return *this;
    }

    TestClass &operator=(TestClass &&right) {
        cout << "move assignment operator =" << endl;
        if (this != &right) {
            delete values;
            values = right.values;
            right.values = nullptr;
        }
        return *this;
    }

    void print() {
        if (values != nullptr)
            cout << "x = " << values[0] << " y = " << values[1] << endl;
    }
private:
    int *values;
};

TestClass rvalue_func() {
    cout << "creating TestClass temp" << endl;
    TestClass temp(100, 200);
    cout << "TestClass temp is created" << endl;
    return temp;
}
void test_rvalues() {
    cout << "-------------vector push back--------------" << endl;
    std::vector<TestClass> test_vector;
    test_vector.push_back(TestClass(1, 2));

    cout << "-----rvalue constructor with std::move-----" << endl;
    TestClass testvar1(std::move(rvalue_func()));

    cout << "------------rvalue constructor-------------" << endl;
    TestClass testvar2(rvalue_func());


    cout << "-------------------------------------------" << endl;
    cout << "testvar2 values ";
    testvar2.print();
}

int main(int argc, char *argv[]) {
    test_rvalues();

    return 0;
}

结果:

-------------vector push back--------------
default constructor x = 1 y = 2
move constructor x = 1 y = 2
-----rvalue constructor with std::move-----
creating TestClass temp
default constructor x = 100 y = 200
TestClass temp is created
move constructor x = 100 y = 200
------------rvalue constructor-------------
creating TestClass temp
default constructor x = 100 y = 200
TestClass temp is created
-------------------------------------------
testvar2 values x = 100 y = 200

这是允许编译器执行的优化,称为copy elision(即使省略了移动构造函数,这似乎也是这个名称)。

基本上,编译器有时被允许(或者,从 C++17 开始,甚至被要求)不调用复制或移动构造函数,如果相反它可以只在将要复制或移动的地方创建对象到。在这种情况下,它知道该对象将进入 testvar2,因此它只是首先在那里创建了对象。

通常只有在符合标准的程序无法区分存在优化和不存在优化之间的区别时才允许编译器优化(例如,将 int 上的算术运算替换为其他产生相同结果但 CPU 计算成本更低的那些)。复制省略是特别允许编译器以您可以区分的方式进行优化的少数情况之一。