I want to overload a common copy-assignment operator normally. At first I used a interface that only requires a const reference to the source, and explicitly disabled the interface that accepts a modifiable reference, but I can not pass the compilation. The compiler reports "error: use of deleted function ‘ClassA& ClassA::operator=(ClassA&)"

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

Why a copy-assignment operation needs a modifiable reference to the source, instead of a const reference? The assignment operation just needs to access the source read-only!

There is a same question about the copy-constructor, I omit it for simplifying.

What's wrong with my code? or we can NOT delete it?

My sample code is following:

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;
};

3 Answers

4
songyuanyao On Best Solutions

or we can NOT delete it?

You just don't need to do that. If you provide a user-defined copy assignment operator, then no other ones will be implicitly-declared, i.e. only the user-defined one will exist.

If you do that, the copy assignment operator you explicitly marked as delete will participate in overload resolution; when it's selected the compilation fails. For ob = oa;, the operator=( ClassA & ) is a better match, if it doesn't exist, operator=( const ClassA & ) will be used and work fine.

So in this case you can just do

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;
   }
};
2
Angew On

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.

You cannot unexpectedly use something which does not exist. If your class defines ClassA & operator=( const ClassA & ), then ClassA & operator=( ClassA & ) will not exist at all (the compiler will not generate that). There's no reason to provide & delete it.

1
Artyer On

If you explicity delete it, and you call:

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

Of course ClassA & operator=( ClassA & ) is the best match as oa is a non-const lvalue. Since it is deleted, it will be an error.

If it is not declared at all, ClassA & operator=( const ClassA & ) now becomes the best match. So it will never try to use ClassA & operator=( ClassA & ) since it does not exist.

If you really wanted to, you could still have ClassA & operator=( ClassA & ) = delete;, and you would manually have to assign from const references:

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

This shows that you don't need a non-const lvalue assignment operator. But there is really no point, as it will be used as a const reference anyways if ClassA & operator=( ClassA & ) is not declared.