Compiler generates default move constructor when it shouldn't

  • A+
Category:Languages

from N3337:

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, X does not have a user-declared destructor, and the move constructor would not be implicitly defined as deleted.

The first question is: why compiler generates move constructor when it shouldn't (there is user declared destructor and other functions which prevents generation of move constructor). Example program below prints constructorCounter=5 which means that there used move constructor (without move operations value::constructorCounter should be 10)

#include <iostream>  class value {     public:         value() {             ++constructorCounter;         }         value(const int value)             : _value(value)         {             ++constructorCounter;         }         value(const value& other)             : _value(other._value)         {             ++constructorCounter;         }         const value& operator=(const value& rhs) {             _value = rhs._value;             return _value;         }         ~value() { }         static int constructorCounter;     private:         int _value; };  int value::constructorCounter = 0;  class array {     public:         //  array() = delete;         //  array(array&&) = delete;         array(const int size)             : _size(size), _values(new value[size])         {             std::clog << "array(const int size)" << std::endl;         }         array(const array& rhs)             : array(rhs._size)         {             std::clog << "array(const array& rhs)" << std::endl;             for (int i = 0; i < _size; ++i)                 _values[i] = rhs._values[i];         }         array& operator=(const array&) {             std::clog << "array& operator=(const array&)" << std::endl;         }         ~array() {             delete [] _values;         }     private:         value* _values;         int _size; };  int main(int argc, char *argv[]) {     array c(array(5));     std::clog << "constructor counter=" << value::constructorCounter << std::endl;      return 0; } 

Second related question: why program fail to compile if i disable move constructor array(array&&) = deleted;, why there is no 'fall back' to copy consturctor array(const array&) ?


At first, the line:

array c(array(5)); 

Leads to the copy constructor being used (because there is no user declared move constructor). However since it happens to be the c object construction, the compiler is able to elide the copy and construct your array object in-place.

Now when you user-declare the move constructor as delete-d, it is considered during overload resolution and indeed while resolving the call in line:

array c(array(5)); 

array(array&&) will be a perfect match... But it is deleted so overload resolution stops (perfect match), and an error is output (the function is deleted).

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: