Mantosh Kumar
Mantosh Kumar

Reputation: 5731

Understanding Default Move Constructor Definition

While reading about the move constructor from the current standard, I can see the following about this:

12.8 Copying and moving class objects

If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared as defaulted if and only if

— X does not have a user-declared copy constructor,
— X does not have a user-declared copy assignment operator,
— X does not have a user-declared move assignment operator, and
— X does not have a user-declared destructor.

[ Note: When the move constructor is not implicitly declared or explicitly supplied, expressions that otherwise would have invoked the move constructor may instead invoke a copy constructor. — end note ]

I think note section clearly mentions that fall-back for default move constructor would be copy constructor. To understand this concept I wrote the small program to understand this concept.

#include<iostream>
struct handleclass {
public:
   handleclass():length{0}, p{nullptr} {}
    handleclass(size_t l):length{l},p{new int[length]} { }  
    ~handleclass() { delete[] p; }
    size_t length;
    int* p;
 };

handleclass function(void) {
    handleclass x(10);
    return x;
}

int main() {
    handleclass y;
    std::cout<<y.length<<std::endl;
    y = function();
    std::cout<<y.length<<std::endl;   
    
   handleclass a;
   handleclass b(10);
   a = std::move(b);

    return 0;
}

Obviously this program is incorrect and would have undefined behaviour(terminate) due to the shallow copy of resources by two objects. But my focus is to understand the default move constructor generated and used in program. I hope this example make sense.

In the above program, in both cases where move constructor should be called, it appears to me that compiler is using default copy constructor.

Based on the above rule mentioned in the standard I think we should have got the compiler error as now program explicitly trying to call the move constructor and neither user has implemented nor compiler generates default(implicitly) as above rule does not satisfy?.

However this is getting compiled without any warning/error and running successfully. Could somebody explains about default(implicitly) move constructor concepts? Or I am missing something?.

Upvotes: 1

Views: 1022

Answers (3)

Chris Drew
Chris Drew

Reputation: 15334

A move constructor and move assignment operator have not been implicitly declared because you have explicitly defined a destructor. A copy constructor and copy assignment operator have been implicitly declared though (although this behaviour is deprecated).

If a move constructor and move assignment operator have not been implicitly (or explicitly) declared it will fall back to using the copy equivalents.

As you are trying to call move-assignment it will fall back to using copy assignment instead.

Upvotes: 2

Mike Seymour
Mike Seymour

Reputation: 254461

Indeed, there is no implicit move constructor due to the user-declared destructor.

But there is an implicit copy constructor and copy-assignment operator; for historical reasons, the destructor doesn't inhibit those, although such behaviour is deprecated since (as you point out) it usually gives invalid copy semantics.

They can be used to copy both lvalues and rvalues, and so are used for the function return (which might be elided) and assignments in your test program. If you want to prevent that, then you'll have to delete those functions.

Upvotes: 2

M.M
M.M

Reputation: 141576

You're forgetting about copy elision which means that y = function(); may not actually invoke any copy or move constructors; just the constructor for x and the assignment operator.

Some compilers let you disable copy elision, as mentioned on that thread.

I'm not sure what you mean by "in both cases where move constructor should be called". There are actually zero cases where move constructor should be called (your object does not have a move constructor), and one case where copy constructor could be called (the return statement) but may be elided.

You have two cases of assignment operator: y = function(); and a = std::move(b); . Again, since your class does not have a move-assignment operator, these will use the copy-assignment operator.

It would probably help your testing if you added code to your object to cout from within the copy constructor and move constructor.

Upvotes: 2

Related Questions