Rvalues с оператором копирования
Рассмотрим этот простой класс
class Foo
{
public:
Foo() = default;
Foo(const Foo &) = default;
Foo & operator=(const Foo & rhs)
{
return *this;
}
Foo & operator=(Foo && rhs) = delete;
};
Foo getFoo()
{
Foo f;
return f;
}
int main()
{
Foo f;
Foo & rf = f;
rf = getFoo(); // Use of deleted move assignment.
return 0;
}
Когда я скомпилирую пример выше, я получаю error: use of deleted function 'Foo& Foo::operator=(Foo&&)'
Из Назначение копирования:
Если предусмотрено только назначение копирования, все категории аргументов выбирают его (если он принимает свой аргумент по значению или как ссылку на константу, так как rvalues могут связываться с ссылками на const), что делает назначение копии резервом для назначения переноса, когда перемещение недоступно.
Почему резервное копирование компилятора не копирует назначение, если ссылка const lvalue может связываться с rvalue и const Foo & f = getFoo();
работает.
Компилятор - gcc 4.7.2.
Ответы
Ответ 1
Нет возврата, концепция называется разрешением перегрузки.
Компилятор выполняет разрешение перегрузки и принимает решение перед тем, как он проверяет, был ли метод удален или нет.
Компилятор решает, что конструктор перемещения является лучшим выбором, затем он определяет, что этот метод был удален, следовательно, ошибка.
Примечание 1:
delete
буквально не удаляет этот метод. Если используется delete
, метод определяется как удаленный, но он все еще может быть найден с помощью разрешения перегрузки.
Из cppreference.com документации (выделение мое):
... разрешение перегрузки происходит сначала, а программа только плохо сформирована, если выбрана удаленная функция.
В вашем примере доступен конструктор перемещения (с точки зрения разрешения перегрузки). Однако он определяется как удаленный.
Примечание 2: Если вы не хотите, чтобы ваш класс имел конструктор перемещения, просто не определяйте его. Компилятор не будет генерировать конструктор перемещения, если вы объявили одно из следующего: копировать конструктор, оператор присваивания копии, оператор присваивания перемещения, деструктор.
Ответ 2
Компилятор делает то, что вы просите. Вы удалили оператор присваивания перемещения, который указывает, что вы не хотите разрешать присвоение из r значений. Оператор присваивания перемещения будет найден во время разрешения перегрузки, а так как он удален, выдается диагностика.
Если вы просто объявите оператор присваивания копий, то оператор присваивания перемещения не будет объявлен неявно, поэтому не будет найден через разрешение перегрузки и вместо этого будет вызываться оператор назначения копирования.
Ответ 3
Цитата, возможно, немного вводит в заблуждение.
Если назначение переноса вообще не объявлено из-за явного объявления назначения копирования, тогда вызов с r-значением "вернется" к присваиванию копии.
Но вы явно указали перенос и удалили его. Таким образом, объявление назначения переноса "доступно" и разрешает удаленное определение.