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?
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";
}