0

I wrote a program as below:

#include <iostream>

using namespace std;

class A {
public:
    A() {
    }
    A(A &a) {
        id = a.id;
        cout << "copy constructor" << endl;
    }
    A& operator=(A &other) {
        id = other.id;
        cout << "copy assignment" << endl;
        return *this;
    }
    A(A &&other) {
        id = other.id;
        cout << "move constructor" << endl;
    }
    A& operator=(A &&other) {
        id = other.id;
        cout << "move assignment" << endl;
        return *this;
    }
public:
    int id = 10;
};

A foo() {
    A a;
    return a;
}

int main()
{
    A a;
    A a2(a); // output: copy constructor
    A a3 = a2; // output: copy constructor
    a3 = a2; // output: copy assignment
    A a4 = foo(); // output: 
    a4 = foo(); // output: move assignment
    return 0;
}

I compiled it on my Mac OS. The output is:

copy constructor
copy constructor
copy assignment
move assignment

My question are:

  1. Why the output of A a4 = foo(); is empty? I thought it should call the move constructor.
  2. Why the output of A a3 = a2; is copy constructor instead of copy assignment?

2 Answers 2

5
  1. Because copies and moves may be elided by the compiler if it wishes. The relevant constructor must still exist, but it is explicitly stated in the standard that they might not be invoked. (This is a rare example of a standard-defined optimisation, in particular allowing Return Value Optimisation to be standard-defined also.)

  2. Because the use of = in initialisation performs construction, not assignment. It's somewhat confusing that this is the syntax. A a3(a2), which is [essentially] equivalent, would be clearer in this regard.

Sign up to request clarification or add additional context in comments.

6 Comments

Thanks your reply. But as for the first point, I am bit confused. If the move constructor might not be invoked, then how could we correctly initialize the l-value as expected? For example, if I rewrite the move constructor as id = other.id * 10;, if it's been elided, how could be get the correct id for a4?
@injoy: Well, that's not how a move constructor is supposed to be written.
Yes, you're correct. BTW, I added one line cout << "default constructor" << endl; in the A(){}. And now the output of A a4 = foo(); is two default constructors. Looks like that move constructor is not been called. Why?
Because, as I said, it does not need to be. The temporary can be used to construct the named object directly, with the potential copy/move elided.
Not calling either the copy ctor or the move ctor is not as efficient as their being elided. Don't "force" the compiler to do anything: it knows what it's doing.
|
0

The compiler is generating default methods for:

A (const A &);
A & operator = (const A &);

If you add the const qualifier to your copy ctor / assign methods, you may see the results you expect.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.