RVO и удаленный конструктор перемещения в C++14

Я узнал о (N) RVO в течение последних нескольких дней. Как я читал о cppreference в статье об исключении копии для С++ 14:

... компиляторам разрешено, но не требуется пропускать конструкцию копирования и перемещения (начиная с С++ 11) объектов класса, даже если конструктор копирования/перемещения (начиная с С++ 11) и деструктор имеют наблюдаемую сторону -эффекты. Это оптимизация: даже если она имеет место и не вызывается конструктор копирования/перемещения, он все равно должен присутствовать и быть доступным (как если бы оптимизация не выполнялась в момент all), в противном случае программа некорректна.

Таким образом, конструктор копирования или перемещения должен присутствовать и быть доступным. Но в приведенном ниже коде:

#include <iostream>

class myClass
{
public:
    myClass() { std::cout << "Constructor" << std::endl; }
    ~myClass() { std::cout << "Destructor" << std::endl; }

    myClass(myClass const&) { std::cout << "COPY constructor" << std::endl;}
    myClass(myClass &&) = delete;
};

myClass foo()
{
    return myClass{};
}

int main()
{
    myClass m = foo();
    return 0;
}

Я получил следующую ошибку: test.cpp: In function 'myClass foo()': test.cpp:15:17: error: use of deleted function 'myClass::myClass(myClass&&)' return myClass{};. Я получаю эту ошибку, даже если я не вызываю foo() из main(). Та же проблема с НРВО.

Следовательно, всегда требуется конструктор перемещения, не так ли? (пока копии нет, я проверял)

Я не понимаю, где компилятору нужен конструктор перемещения. Мое единственное предположение состоит в том, что это может потребоваться для создания временной переменной, но это звучит сомнительно. Кто-нибудь знает ответ?

О компиляторе: пробовал на компиляторах g++ и VS, проверить можно онлайн: http://rextester.com/HFT30137< /а>.

P.S. Я знаю, что в стандарте С++ 17 RVO обязателен. Но NRVO — нет, поэтому я хочу изучить, что здесь происходит, чтобы понять, когда я могу использовать NRVO.


person Annie    schedule 03.02.2018    source источник
comment
Компилируется как C++ 17 с gcc 7.2   -  person    schedule 03.02.2018
comment
Эмпирическое правило: никогда не удаляйте явно операции перемещения. Вместо этого определите или удалите операции копирования явно, что подавляет генерацию перемещений. Единственный случай, когда явное удаление операций перемещения имеет значение, — это когда вы получаете патологический копируемый, но не перемещаемый тип.   -  person Casey    schedule 03.02.2018
comment
@Casey, так что единственный случай, когда можно удалить оператор перемещения, это когда вы удаляете и копию, и перемещение? Или в этом случае удаление оператора перемещения излишне?   -  person Annie    schedule 03.02.2018


Ответы (1)


Процитировано из cppreference:

Удаленные функции

Если вместо тела функции использовать специальный синтаксис = delete ; используется, функция определяется как удаленная.

...

Если функция перегружена, сначала выполняется разрешение перегрузки, и программа имеет неправильный формат только в том случае, если была выбрана удаленная функция.

Это не то же самое, если вы явно определяете удаляемый конструктор перемещения. Здесь из-за наличия этого удаленного конструктора перемещения, хотя конструктор копирования может совпадать, конструктор перемещения лучше, поэтому конструктор перемещения выбирается во время разрешения перегрузки.

Если вы удалите явный delete, будет выбран конструктор копирования, и ваша программа будет скомпилирована.

person llllllllll    schedule 03.02.2018
comment
Хороший ответ, но я рекомендую отредактировать, чтобы преуспеть в том, что, хотя конструктор копирования есть и хорош, конструктор перемещения лучше подходит и выбирается вместо конструктора копирования. - person user4581301; 03.02.2018
comment
Спасибо. Раньше я писал явное delete, чтобы запретить использование конструктора по умолчанию. Как я понимаю, если бы я определил конструктор копирования, конструктор перемещения по умолчанию не был бы создан. Это? - person Annie; 03.02.2018
comment
@user4581301 user4581301 Существует объявленный пользователем конструктор копирования, поэтому конструктор перемещения не объявлен неявно, поэтому будет вызываться конструктор копирования. - person Holt; 03.02.2018
comment
@ user4581301 Ответ обновлен с простым объяснением. На самом деле здесь возвращаемое значение является значением r, результат легко предсказать. Но даже если в функции OP возвращается lvalue, все равно нужно выбрать конструктор перемещения. Может быть, это слишком длинно, чтобы цитировать весь этот материал к ответу. - person llllllllll; 03.02.2018
comment
@ AnyaMitrushchienkova Да, компилятор не сгенерирует. - person llllllllll; 03.02.2018
comment
@liliscent Круто, я этого не знал. Я слышал правило, что если вы определили конструктор копирования, вы также должны определить оператор присваивания копии и деструктор. Я думал, что это также верно для операторов перемещения. - person Annie; 03.02.2018
comment
@AnyaMitrushchienkova вы пишете о Правиле Трех. Есть еще два правила, метко названные Правилами пяти и нуля, которые охватывают остальные основы. Вот ссылка на обсуждение всех трех правил и их связи. - person user4581301; 03.02.2018