Подтвердить что ты не робот

Почему мой объект не копируется при удалении конструктора перемещения?

Я пытаюсь использовать этот код, чтобы продемонстрировать использование copy-constructor. Моя презумпция заключалась в том, что когда у меня есть функция, возвращающая значение, мой компилятор будет по умолчанию выполнять перемещение объекта. Но когда конструктор move недоступен, компилятор будет копировать вместо него (в С++ 03 компилятор будет копировать при возврате по значению). Итак, почему в следующем примере компилятор пытается вызвать явно удаленный move-constructor вместо доступного конструктора-копии? Я компилирую это в GCC 4.7.2.

struct S
{
    S() = default;
    S(S const &) = default;
    S(S&&) = delete;
};

S f() { return S{}; }

int main()
{
    f();
}

prog.cpp: В функции ‘S f()’:
prog.cpp: 8: 18: ошибка: использование удаленной функции ‘S::S(S&&)’
prog.cpp: 5: 5: error: заявлено здесь

4b9b3361

Ответ 1

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

Удаление специального члена - это не то же самое, что не иметь специального члена. Нигде это более очевидно, чем с конструктором перемещения и оператором присваивания перемещения.

При наличии, независимо от того, были ли удалены, дефолты или определены пользователем, конструктор перемещения и оператор назначения перемещения участвуют в разрешении перегрузки. Это означает, что они "конкурируют" со специальными членами экземпляра. Члены копии обычно предпочитают константы lvalues, тогда как элементы перемещения привлекают rvalues.

При возврате локального типа из функции (когда локальный тип является тем же самым не-cv-квалифицированным типом, что и тип возврата), оператор return сначала рассматривает возвращаемое выражение как rvalue, и только если он не может найти подходящий конструктор, тогда он будет считаться lvalue. То есть сопоставление соответствующего конструктора для возврата локального объекта из функции является двухфазной.

Если у вас нет конструктора перемещения вообще (даже не удалено), но у вас есть обычный конструктор копирования (принимает const &), тогда rvalue из оператора return будет соответствовать конструктору копирования.

Если у вас do есть конструктор перемещения, даже он помечен как удаленный, значение rvalue из оператора return найдет конструктор перемещения более подходящим, чем конструктор копирования.

Резюме

Если вы действительно не знаете, что делаете, никогда удалять перемещаемые элементы. Если вы не хотите, чтобы ваш тип был подвижным, просто не определяйте участников перемещения и убедитесь, что вы объявляете экземпляры копирования, даже если членами копии являются =default 'd.

Обновление

Я полагаю, что трудно предоставить цитаты из Стандарта о том, что удалить не делает? - DyP

8.4.3 Удаленные определения [dcl.fct.def.delete]

2 Программа, которая ссылается на удаленную функцию неявно или явно, кроме как заявить об этом, плохо сформирован. [Примечание: это включает вызов функции неявно или явно и формирование указатель или указатель на элемент к функции. Это применимо даже для ссылки в выражениях, которые не потенциально оцениваются. Если функция перегружена, на нее ссылаются только в том случае, если функция выбирается с помощью разрешения перегрузки. - конечная нота]

Обновление 2

12.8 Копирование и перемещение объектов класса [class.copy]

9 Если определение класса X явно не объявляет движение конструктор, одно будет объявлено как неявное, если и только если

  • X не имеет объявленного пользователем конструктора копирования,
  • X не имеет объявленного пользователем оператора назначения копирования,
  • X не имеет объявленного пользователем оператора назначения перемещения и
  • X не имеет объявленного пользователем деструктора.

[Примечание: когда конструктор перемещения неявно объявлен или явно предоставленные выражения, которые в противном случае вызывали бы move constructor может вместо этого ссылаться на конструктор копирования. - конечная нота]