Компилятор не генерирует конструкторы перемещения - PullRequest
0 голосов
/ 14 ноября 2018

Я пытаюсь понять, что семантика перемещения ищет конструкторы перемещения, сгенерированные компилятором (копирование и назначение).В Modern Effective C ++ Скотт Мейерс говорит в пункте № 17, что если не объявлены явные конструкторы копирования, компилятор сгенерирует конструкторы перемещения, которые будут выполнять перемещение по элементам для non-static членов.

Чтобы подтвердить это, я пытаюсь использовать следующий код:

#include <iostream>
#include <string>
using namespace std;

class A
{
private:
    std::string str;

public:

    A() : str("Init string")
    {
        cout << "Default constructor" << endl;
    }

    A(std::string _str) : str(_str)
    {
        cout << "Constructor with string" << endl;
    }

    std::string getString()
    {
        return str;
    }
};

int main() {

    A obj1;
    A obj2("Obj2 string");

    cout << endl;
    cout << "obj1: " << obj1.getString() << endl;
    cout << "obj2: " << obj2.getString() << endl;

    obj1 = std::move(obj2);

    cout << endl;
    cout << "obj1: " << obj1.getString() << endl;
    cout << "obj2: " << obj2.getString() << endl;

    return 0;
}

Вывод:

Default constructor
Constructor with string

obj1: Init string
obj2: Obj2 string

obj1: Obj2 string
obj2: Obj2 string

Но я ожидал, что это будет:

Default constructor
Constructor with string

obj1: Init string
obj2: Obj2 string

obj1: Obj2 string
obj2: 

Поскольку obj2.str был бы перемещен и теперь имеет пустую строку.

По какой причине компилятор не генерирует конструктор назначения перемещения и не вызывает оператор копирования копии?

РЕДАКТИРОВАТЬ: Реализация оператора назначения перемещения, как показано ниже, дает ожидаемый результат (т. Е. Пустую строку после вызова std :: move)

A& operator=(A&& obj)
    {
        cout << "Move assignment operator" << endl;
        str = std::move(obj.str);
        return *this;
    }

Ответы [ 3 ]

0 голосов
/ 14 ноября 2018

Компилятор вызывает оператор присваивания перемещения, в результате чего obj1.str присваивается перемещение из obj2.str. Однако перемещение не гарантирует, что исходный объект пуст; для большинства стандартных библиотечных классов объект, из которого был перемещен, остается в «допустимом, но неопределенном состоянии». (Самым очевидным исключением является то, что std::unique_ptr<T>, который был перемещен, гарантированно будет нулевым.) Часто, но не всегда, случается, что перемещенный из std::string является пустым. В вашем случае строка "Obj2 string" достаточно короткая, чтобы ее можно было хранить в строке (, т.е. , используя оптимизацию короткой строки). Если это так, то оператор присваивания перемещения должен скопировать строку. Если вернуться назад и очистить исходную строку, добавит дополнительные издержки , поэтому реализация этого не сделает.

0 голосов
/ 14 ноября 2018

Во-первых, obj1 = std::move(obj2); вызывает оператор присваивания, поэтому он не имеет ничего общего с конструкторами.

Да, компилятор генерирует оператор присваивания перемещения для A, который выполняет операцию перемещения по элементам, включая элемент данных str. Проблема в том, что после операции перемещения str остается в допустимом, но неопределенном состоянии . Также см. std::basic_string::operator=.

Заменяет содержимое на str, используя семантику перемещения. str после этого находится в допустимом, но неопределенном состоянии.

Я думаю, вы могли бы наблюдать тот же результат только с std::string, например,

std::string str1 = "Init string";
std::string str2 = "Obj2 string";
str1 = std::move(str2);
std::cout << str2;

LIVE с лязгом , только для справки; он дает результат, как вы ожидали, но все еще помните, что результат не указан.

0 голосов
/ 14 ноября 2018

Стандарт не определяет состояние перемещенного объекта.

17.6.5.15 Состояние библиотек, перемещенных из [Lib.types.movedfrom]

Объекты типов, определенных в стандартной библиотеке C ++, могут быть перемещены из (12.8). Операции перемещения могут быть явно указаны или неявно сгенерированы. Если не указано иное, такие перемещенные объекты должны быть помещены в действительное, но неопределенное состояние.

Таким образом, ваш перемещенный из строки находится в допустимом, но неопределенном состоянии. Не за исключением пустой строки или того же значения или строки, содержащей "potato".

Я предполагаю, что "Obj2 string" подходит для оптимизации небольших строк, которые позволяют небольшим строкам жить в стеке, а не в куче. В этом конкретном случае присваивание memcpy строкового объекта другому без какой-либо очистки (например, без установки старой строки на пустой) на самом деле быстрее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...