C++ declare a move/copy operation will suppress generation of related operations?

374 views Asked by At

I saw a sentence in The C++ programing language which I'm confused with:

• If the programmer declares a copy operation, a move operation, or a destructor for a class,no copy operation, move operation, or destructor is generated for that class.

I wrote a test code shown below:

#include <iostream>
using namespace std;
class A
{
public:
    A() :a(0){}
    A(int _a) :a(_a){}
    int get() const
    {
        return a;
    }
    /*A& operator=(const A &x)
    {
        a = x.a;
        cout << "A copy assignment" << endl;
        return *this;
    }*/
    A& operator=(A&&x)
    {
        cout << "A move assignment" << endl;
        swap(a, x.a); 
        return *this;
    }
private:
    int a;
};

A operator+(const A &x, const A &y)
{
    A temp{ x.get() + y.get() };
    return temp;
}

int main() 
{
    A a1(1), a2(2), a3;
    a3 = a1;
    cout << a3.get() << endl;
    return 0;
}

The result is: enter image description here

I define a move assignment, there should be not default copy assignment generated as said in the book, but how could a3 gets the copy of a1?

Another question:

I modified a3 assignment expression:

a3 = a1+a2;

the result is: enter image description here

Then I comment out the move assignment and remove comment on copy assignment:

A& operator=(const A &x)
{
    a = x.a;
    cout << "A copy assignment" << endl;
    return *this;
}
/*
A& operator=(A&&x)
{
    cout << "A move assignment" << endl;
    swap(a, x.a); 
    return *this;
}*/

the result is: enter image description here

how could copy assignment be called? The result of a1+a2 is a rvalue, how could this rvalue be passed to copy assignment whose argument is const A&? Forgive my confusion about rvalue

any help is appreciated!

3

There are 3 answers

0
eerorika On BEST ANSWER

I define a move assignment, there should be not default copy assignment generated as said in the book

Correct.

but how could a3 gets the copy of a1?

It couldn't according to the standard. If the compiler does not give you a diagnostic message for this, then the compiler doesn't conform to the standard.


The result of a1+a2 is a rvalue

Correct.

how could this rvalue be passed to copy assignment whose argument is const A&?

Because rvalues can be bound to lvalue references to const. The lifetime of the temporary object is extended to match the potential lifetime of the reference. That is for the duration of the function in the case of a reference argument.

0
Bryce On

This statement is forward-looking and concise. It has not yet fully implemented the C++ 11 standard, but it will come sooner or later.

C++11 standard section 12.8.7 quotes:

If the class definition does not explicitly declare a copy constructor, one is declared implicitly. If the class definition declares a move constructor or move assignment operator, the implicitly declared copy constructor is defined as deleted; otherwise, it is defined as defaulted (8.4). The latter case is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.

0
jonei On

Based on some practical experiments with GCC 6 and that (somewhat confusing) sentence, this is what I found:

  • If you declare any other constructor for a class, the default constructor is not defined.
  • If you declare a copy operation (constructor or assignment operator), the other copy operation is implicitly generated. However, this generation is deprecated from the ISO standard, as described in the paragraph following the sentence in the book.
  • If you declare a destructor, copy operations are implicitly generated. This is deprecated as well.
  • If you declare a copy operation, move operations are implicitly deleted (defaulting to copy opertions as seen below).
  • If you declare a move operation, the other move operation is implicitly deleted.
  • If you declare a move operation, copy operations are implicitly deleted.
  • If you declare a destructor, any code which used implicit move operations instead use implicit copy operations (or explicit ones if defined). However, this might be implicitly deprecated from what learnt above.
  • If no explicit destructor has been defined, a default (empty) destructor seems to always be generated for a class, no matter which other operations have or have not been defined.