Skip to content

Copy-Move Elision

Copy-move elision is a compiler optimization that eliminates unnecessary copy and move operations, even when they would have observable side effects. This optimization is mandatory in C++17 and later, meaning the compiler must perform it when possible.

Return Value Optimization (RVO)

RVO eliminates the copy/move of a temporary object when returning from a function.

Named Return Value Optimization (NRVO)

NRVO is a specific form of RVO that applies when returning a named local variable.

cpp
// Without NRVO (conceptually):
Widget createWidget() {
    Widget w;  // Construct w
    w.setValue(42);
    return w;  // Copy w to return value
}

// With NRVO (what actually happens):
Widget createWidget() {
    Widget w;  // Construct w directly in return location
    w.setValue(42);
    // No copy - w is already in the right place
}

Unnamed Return Value Optimization

Applies when returning a temporary object:

cpp
Widget createWidget() {
    return Widget(42);  // No copy - constructed directly in return location
}

Copy Elision in Different Contexts

1. Function Return Values

cpp
class ExpensiveObject {
    std::vector<int> data;
public:
    ExpensiveObject() : data(1000000) {}
    ExpensiveObject(const ExpensiveObject& other) : data(other.data) {
        std::cout << "Copy constructor called!" << std::endl;
    }
    ExpensiveObject(ExpensiveObject&& other) noexcept : data(std::move(other.data)) {
        std::cout << "Move constructor called!" << std::endl;
    }
};

ExpensiveObject createObject() {
    ExpensiveObject obj;  // NRVO applies here
    return obj;  // No copy/move - obj is constructed in return location
}

int main() {
    ExpensiveObject result = createObject();  // No copy/move
    return 0;
}

2. Exception Handling

cpp
void throwObject() {
    ExpensiveObject obj;
    throw obj;  // Copy elision applies to thrown objects
}

3. Initialization with Temporary

cpp
ExpensiveObject obj = ExpensiveObject();  // Copy elision applies

When Copy Elision Cannot Be Applied

1. Multiple Return Paths

cpp
ExpensiveObject createObject(bool flag) {
    if (flag) {
        ExpensiveObject obj1;
        return obj1;  // NRVO applies
    } else {
        ExpensiveObject obj2;
        return obj2;  // NRVO applies
    }
    // But if we had:
    // ExpensiveObject obj1, obj2;
    // if (flag) return obj1;
    // else return obj2;
    // NRVO might not apply due to multiple return paths
}

2. Return by Reference

cpp
ExpensiveObject& createObject() {
    static ExpensiveObject obj;  // Must return reference
    return obj;  // No elision possible
}

3. Virtual Functions

cpp
class Base {
public:
    virtual ExpensiveObject create() = 0;
};

class Derived : public Base {
public:
    ExpensiveObject create() override {
        ExpensiveObject obj;
        return obj;  // NRVO applies
    }
};

Performance Implications

  1. Latency Reduction: Eliminating unnecessary copies reduces CPU cycles
  2. Memory Bandwidth: Fewer memory operations mean lower latency
  3. Cache Efficiency: Less memory movement improves cache performance
  4. Predictable Performance: Consistent optimization across compilers