Почему 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;
};

зачем вам это нужно? почему изменение себя изменит другой экземпляр?

OznOg 28.05.2019 11:05

Принятие ссылки const rvalue не имеет особого смысла из-за характера ссылок rvalue.

Fureeish 28.05.2019 11:07

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

Peter 28.05.2019 12:07
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
119
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

or we can NOT delete it?

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

Если вы это сделаете, оператор присваивания копии, который вы явно пометили как 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;
   }
};

Of course, I can get compiled if I don't expicitly delete the interface, but that is not my purpose. I'd like to expicitly delete it, to avoid unexpected using it.

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

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

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 & ) не объявлен.

Другие вопросы по теме