Consider this class
class A {
public:
tracker tra;
A(tracker _t) : tra(_t) {}
};
And call it through
A a {tracker()};
The object created by tracker()
is never used until being stored in a.tra
Why don't the compiler optimize all the copy constructions away?
.
The tracker is defined here:
class tracker {
public:
void mark(const char* v) {
std::cout << v << ' ' << this << std::endl;
}
tracker() {
mark("con");
}
tracker(const tracker& o) {
mark("cpy");
}
tracker(tracker&& o) {
mark("mov");
}
~tracker() {
mark("des");
}
tracker& operator=(const tracker&) {
mark("=cp");
return *this;
}
tracker& operator=(tracker&&) {
mark("=mv");
return *this;
}
};
The compiler can't optimize out the copy construction of tracker
in this case because the copy constructor and the destructor of tracker
has observable side-effects. If the compiler optimizes out the copy construction ignoring that, it will change the observable behavior of the program thus violating the as-if rule.
There are exception to the as-if rule that allows the compiler to optimize out the copy/move construction even if the copy/move constructor and/or the destructor have observable side-effects. (copy elision)
But that exception rule is only applicable in some cases, and your code is not one of those. You are (copy-)constructing the member variable tra
with the lvalue of type tracker
. This is not the case that's mentioned in the copy elision rule.