Почему GCC отказывается от ссылки const в операции присваивания копии?

Я хочу нормально перегрузить общий оператор присваивания копии. Сначала я использовал интерфейс, который требует только константную ссылку на источник, и явно отключил интерфейс, который принимает модифицируемую ссылку, но не могу пройти компиляцию. Компилятор сообщает "ошибка: использование удаленной функции ClassA& ClassA::operator=(ClassA&)"

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

Почему для операции копирования-присваивания требуется модифицируемая ссылка на источник, а не постоянная ссылка? Операция присваивания просто должна получить доступ к источнику только для чтения!

Тот же вопрос про копи-конструктор, опускаю для упрощения.

Что не так с моим кодом? или мы НЕ можем удалить его?

Мой пример кода следующий:

class ClassA {
public:
   ClassA() = default;
   ClassA( ClassA & ) = default;

   ClassA & operator=( ClassA & )
   = delete   // Must comment-out this, or we can't pass the compilation.
   // { cout << "ClassA & operator=( ClassA & ) executed." << endl; return *this; }
   ;

   ClassA & operator=( ClassA && ) {
      cout << "ClassA & operator=( ClassA && ) executed." << endl;
      return *this;
   };

   ClassA & operator=( const ClassA & ) {
      cout << "ClassA & operator=( const ClassA & ) executed." << endl;
      return *this;
   };
   ClassA & operator=( const ClassA && ) {
      cout << "ClassA & operator=( const ClassA && ) executed." << endl;
      return *this;
   };
};

int main() {
   ClassA oa, ob;
   ob = oa;

   return EXIT_SUCCESS;
};

person Leon    schedule 28.05.2019    source источник
comment
зачем вам это нужно? почему изменение себя изменит другой экземпляр?   -  person OznOg    schedule 28.05.2019
comment
Принятие ссылки const rvalue не имеет особого смысла из-за характера ссылок rvalue.   -  person Fureeish    schedule 28.05.2019
comment
Вам нужно принять решение. Либо используйте ClassA &operator=(const ClassA &) = delete, который не позволяет компилятору генерировать operator=() и означает, что у класса его нет, либо вы определяете и реализуете оператор. Эти два понятия являются взаимоисключающими. Вы не можете ожидать, что оператор будет явно удален, а затем явно реализован.   -  person Peter    schedule 28.05.2019


Ответы (3)


или мы НЕ можем удалить его?

Вам просто не нужно этого делать. Если вы предоставляете пользовательский оператор присваивания копии, то никакие другие не будут неявно объявленный, то есть будет существовать только определенный пользователем.

Если вы это сделаете, оператор присваивания копии, который вы явно пометили как delete, будет участвовать в разрешении перегрузки; когда он выбран, компиляция завершается неудачно. Для ob = oa; лучше подходит operator=( ClassA & ), если он не существует, operator=( const ClassA & ) будет использоваться и работать нормально.

Итак, в этом случае вы можете просто сделать

class ClassA {
public:

   ClassA & operator=( ClassA && ) {
      cout << "ClassA & operator=( ClassA && ) executed." << endl;
      return *this;
   }

   ClassA & operator=( const ClassA & ) {
      cout << "ClassA & operator=( const ClassA & ) executed." << endl;
      return *this;
   }
};
person songyuanyao    schedule 28.05.2019

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

Вы не можете неожиданно использовать то, чего не существует. Если ваш класс определяет ClassA & operator=( const ClassA & ), то ClassA & operator=( ClassA & ) вообще не будет существовать (компилятор этого не сгенерирует). Нет причин предоставлять и удалять его.

person Angew is no longer proud of SO    schedule 28.05.2019

Если вы явно удалите его и вызовете:

ob = oa;
// The same as
ob.operator=(oa);

Конечно, ClassA & operator=( ClassA & ) является лучшим совпадением, так как oa является неконстантным lvalue. Так как он удален, это будет ошибкой.

Если он вообще не объявлен, ClassA & operator=( const ClassA & ) теперь становится лучшим соответствием. Поэтому он никогда не будет пытаться использовать ClassA & operator=( ClassA & ), поскольку его не существует.

Если бы вы действительно хотели, у вас все еще мог бы быть ClassA & operator=( ClassA & ) = delete;, и вам пришлось бы вручную назначать ссылки из констант:

ob = static_cast<const ClassA&>(oa);
// Will now select `ClassA & operator=( const ClassA & )`

Это показывает, что вам не нужен неконстантный оператор присваивания lvalue. Но на самом деле в этом нет смысла, так как он все равно будет использоваться как ссылка на константу, если ClassA & operator=( ClassA & ) не объявлено.

person Artyer    schedule 28.05.2019