c++move-constructorcopy-elision

Copy elision in initializer list?


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

Solution

  • 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.