Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ copy elision of fields

I am trying to get copy elision to work for fields of the object that is to be returned.

Example code:

#include <iostream>

struct A {
    bool x;
    A(bool x) : x(x) {
        std::cout << "A constructed" << std::endl;
    }
    A(const A &other) : x(other.x) {
        std::cout << "A copied" << std::endl;
    }
    A(A &&other) : x(other.x) {
        std::cout << "A moved" << std::endl;
    }
    A &operator=(const A &other) {
        std::cout << "A reassigned" << std::endl;
        if (this != &other) {
            x = other.x;
        }
        return *this;
    }
};

struct B {
    A a;
    B(const A &a) : a(a) {
        std::cout << "B constructed" << std::endl;
    }
    B(const B &other) : a(other.a) {
        std::cout << "B copied" << std::endl;
    }
    B(B &&other) : a(other.a) {
        std::cout << "B moved" << std::endl;
    }
    B &operator=(const B &other) {
        std::cout << "B reassigned" << std::endl;
        if (this != &other) {
            a = other.a;
        }
        return *this;
    }
};

B foo() {
    return B{A{true}};
}


int main() {
    B b = foo();
    std::cout << b.a.x << std::endl;
}

I compile with: g++ -std=c++17 test.cpp -o test.exe

output:

A constructed
A copied
B constructed
1

B is constructed in-place. Why is A not? I would at least expect it to be move-constructed, but it is copied instead.

Is there a way to also construct A in-place, inside the B to be returned? How?

like image 937
PEC Avatar asked Jan 28 '26 01:01

PEC


1 Answers

Constructing a B from an A involves copying the A - it says so in your code. That has nothing to do with copy elision in function returns, all of this happens in the (eventual) construction of B. Nothing in the standard allows eliding (as in "breaking the as-if rule for") the copy construction in member initialization lists. See [class.copy.elision] for the handful of circumstances where the as-if rule may be broken.

Put another way: You get the exact same output when creating B b{A{true}};. The function return is exactly as good, but not better.

If you want A to be moved instead of copied, you need a constructor B(A&&) (which then move-constructs the a member).

like image 122
Max Langhof Avatar answered Jan 29 '26 14:01

Max Langhof



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!