Search code examples
c++constructordefaultmove

gcc/clang C++: move constructors not called but required in a code like A a, b; A c(a+b);


I have this code:

#include <iostream>
using namespace std;

class A{
  char a;
public:
  A(): a('#') { cerr << "DEFAULT!" << endl; };
  A(char a): a(a) { cerr << "VALUE! " << a << endl;  };
  A(A const &o): a(o.a) { cerr << "COPY! " << o.a << endl; }
  // A(A && o): a(std::move(o.a)) { cerr << "MOVE!: " << o.a << endl; }
  // A(const A && o): a(std::move(o.a)) { cerr << "MOVE!(const): " << o.a << endl; }
  // A(volatile A && o): a(std::move(o.a)) { cerr << "MOVE!(volatile): " << o.a << endl; }
  // A(const volatile A && o): a(std::move(o.a)) { cerr << "MOVE!(const volatile): " << o.a << endl; }
  // A(A && o) = delete; // (***)
  friend A operator+(const A &o1, const A &o2){ A r; r.a = o1.a -'a' + o2.a; cerr << "+:" << r << endl; return r; }
  friend ostream& operator<<(ostream& out, const A& o) { out << o.a; return out; }
};

int main(){
  A a('a');
  A b('b');
  A c('c');
  cout << a << b << c << endl;
  A d(b+c); // <----- !!! WHO HANDLES THIS??? !!!
  A e(a);
  cout << a << b << c << d << e << endl;
  return 0;
}

In the line with 'WHO HANDLES THIS???' the magic happens, as the value is correctly transferred.

I assumed the default move constructor was called, so I overloaded it (and then all of them - see 4 commented lines), but to my surprise, no one get the call (if uncommented). Then I tried to delete the default copy constructor (anyway, according to https://en.cppreference.com/w/cpp/language/move_constructor it should not be created by default, as I have copy constructor). This makes g++/clang++ to return error in WHO HANDLES THIS??? line, with message that default move constructor is deleted. clang version 10.0.0-4ubuntu1, g++ (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0.

What the hell is happening here? Can anyone explain or point out to any specs?


Solution

  • The HANDLER is easier to see if you add object addresses to your trace output.

    This is due to one of the optimizations allowed for in the C++ standard. Someday, it may even be required by the C++ standard. (Updated, thanks Artyer.)

    #include <iostream>
    
    using std::cerr;
    using std::cout;
    using std::ostream;
    
    class A { 
        char ch{ '#' };
    public:
        A() { cerr << "DEFAULT!\n"; }
        A(char ch_) : ch{ch_} { cerr << "VALUE! " << ch << "\n";  }
        A(A const& from) : ch{from.ch} { cerr << "COPY! " << ch << "\n"; }
    
        friend auto operator+(A const& a, A const& b) -> A { A r; r.ch = a.ch -'a' + b.ch; cerr << "+:" << r << " @ " << &r << "\n"; return r; }
        friend auto operator<<(ostream& out, const A& a) -> ostream& { return out << a.ch; }
    };
    
    int main() {
        A a{'a'};
        A b{'b'};
        A c{'c'};
        cout << a << b << c << "\n";
        cout << "About to make a d...\n";
        A d{b+c}; // <----- !!! WHO HANDLES THIS??? !!!
        cout << "...d@" << &d << " has been made!\n";
        A e{a};
        cout << a << b << c << d << e << "\n";
    }