Вызов перемещающего конструктора

Рассмотрим тест 1:

class MyClass
{
private:
    string data;
public:
        MyClass()
    { 
        std::cout << "MyClass()" << std::endl; 
    }
    MyClass(string data) : data(data)
    {
        std::cout << "MyClass(string data)"<< std::endl;
    }
    ~MyClass() {}
    MyClass(const MyClass& obj) // copy constr
    {
        std::cout << "MyClass(const MyClass& obj)" << std::endl;
    }
    MyClass(MyClass&& obj) // move constr
    {
        std::cout << "Move constructor. MyClass(MyClass&& obj)" << std::endl;
    }
};

int main()
{
    MyClass obj = move(MyClass("privet"));
    cout << "-----------------------------" << endl;
    MyClass ob = MyClass("hello");
    return 0;
}

Вывод:

MyClass(string data)
Move constructor. MyClass(MyClass&& obj)
-----------------------------
MyClass(string data)

Не понимаю почему в строке MyClass ob = MyClass("hello"); срабатывает вызов только конструктора. Я ожидал увидеть вызов конструктора для временного объекта MyClass("hello"), а затем вызов конструктора перемещения для MyClass ob, который в качестве параметра принимает rvalue reference данного временного объекта, как и в случае с MyClass obj = move(MyClass("privet"));

Рассмотрим тест 2:

class A
{
public:
    A()
    {
        std::cout << "A()" << std::endl;
    }
    A(const A& other)
    {
        std::cout << "Copy ctor\n";
    }

    A(A&& other)
    {
        std::cout << "Move ctor\n";
    }
};

void first(A a) {}

void second(A&& a) {}

int main()
{
    first(A{});
    std::cout << "Let's call the second:\n";
    second(A{});
    return 0;
}

Вывод:

A()
Let's call the second:
A()

Не понимаю как тут вызываются конструкторы. Почему только по одному на функцию, должны же вызваться по конструктору для временных объектов и, в случае с функцией first создание еще одного локального объекта - копии, в случае с функцией second без понятия


Ответы (1 шт):

Автор решения: HolyBlackCat

Вы все понимаете правильно, но компилятору разрешается оптимизировать такие вещи. Это называется copy elision. У GCC есть флаг -fno-elide-constructors, который это отключает.

Начиная с C++17 эта оптимизация стала обязательной (guaranteed copy elision), так что теперь даже A a = A(A(A())); всегда должно быть эквивалентно A a;.


Еще:

MyClass(string data) : data(data) стоит заменить на MyClass(string data) : data(std::move(data)).

→ Ссылка