Странное поведение - компилятор игнорирует конструктор копирования и пытается вызвать удаленный конструктор перемещения
Рассмотрим следующий код (реальный код несколько сложнее и находится под NDA):
class NonMovable
{
public:
NonMovable() = default;
NonMovable(const NonMovable&) noexcept = default;
NonMovable(NonMovable&&) noexcept = delete;
NonMovable& operator=(const NonMovable&) noexcept = default;
NonMovable& operator=(NonMovable&&) noexcept = delete;
// ...
};
NonMovable SomeFunction()
{
NonMovable nonMovable;
// ...
return nonMovable;// Error, NonMovalbe(NonMovable&&) is deleted.
}
Я не могу понять, почему возникает эта ошибка. У NonMovable есть стандартный конструктор копирования, но компилятор пытается вызвать конструктор перемещения, который удален.
Является ли это ошибкой компилятора? Или, может быть, возможен еще какой-то фактор, который принуждает компилятор к такому решению?
Вот так это выглядит. Ошибка наблюдается на разных версиях Visual Studio и компиляторов. 
Ответы (2 шт):
В С++17 ввели правило, по которому для возвращаемых безымянных объектов конструктор копирования или перемещения не должен вызываться и даже не обязан быть объявлен. Это такая обязательная оптимизация (RVO - return value optimization), даже если в теле конструктора выполняется какой-то видимый код. Однако, в вашем случае объект не безымянный (не создан при вызове return), а значит, правило RVO не применимо. В данном случае может происходить оптимизация NRVO - Named RVO, но её исполнение не стандартизовано.
Для устранения ошибки в вашем случае можно просто обернуть имя возвращаемой переменной в фигурные скобки, вызвав тем самым конструктор копирования до return. Но а при return уже сработает обязательная оптимизация RVO:
return {nonMovable};
Похоже, что вариант с фигурными скобками (list-initialization) не работает должным образом в msvc (по-прежнему хочет вызвать конструктор перемещения), и нужно явно указать вызов копирующего конструктора.
return NonMovable(nonMovable);
Clang, GCC устраивают оба варианта.
Вообще, стоит заметить, что при наличии копирующего конструктора в классе, автоматической генерации перемещающего конструктора не происходит. Т.е. если убрать эти строки:
NonMovable(NonMovable&&) noexcept = delete;
NonMovable& operator=(NonMovable&&) noexcept = delete;
класс не станет вдруг перемещаемым, а останется по-прежнему "NonMovable". И в этой ситуации msvc будет вполне корректно себя вести даже с первичным вариантом кода.
Если не определить NonMovable(NonMovable&&) явно и не поставить NonMovable(NonMovable&&) = default, то конструктор перемещения не будет создан, а будет использоваться конструктор копирования. = delete применять к конструктору перемещения бессмысленно.
И даже если написать NonMovable(NonMovable&&) = default; то конструктор копирования по умолчанию будет представлять из себя побайтовое копирование данных.
Можно и нужно эту строку убрать.