https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83032
Bug ID: 83032 Summary: Copy elision for returning subobject Product: gcc Version: 8.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: middle-end Assignee: unassigned at gcc dot gnu.org Reporter: antoshkka at gmail dot com Target Milestone: --- Following code struct trace { trace(trace const &) noexcept; ~trace(); }; struct ts { trace a, b; }; ts foo(); trace testing() { return foo().a; } Compiles into assembly that calls copy constructor and destructors for `trace` objects returned from `foo()`. This could be optimized: return from `foo()` already puts `a` into a correct place for return from `testing()`. So all we need is no call destructor for `b`. Optimal assembly should look close to the following: testing(): push rbx mov rbx, rdi sub rsp, 16 call foo() lea rdi, [rsp+15] call trace::~trace() add rsp, 16 mov rax, rbx pop rbx ret Current suboptimal assembly looks like: testing(): push rbx mov rbx, rdi sub rsp, 16 lea rdi, [rsp+14] call foo() lea rsi, [rsp+14] mov rdi, rbx call trace::trace(trace const&) <== Avoid this lea rdi, [rsp+15] call trace::~trace() lea rdi, [rsp+14] call trace::~trace() <== Avoid this add rsp, 16 mov rax, rbx pop rbx ret This optimization is very useful for C++ code, where returning std::pair, std::tuple, std::variant or std::optional is a common practice. For example following code could use copy elision for std::string inside std::optional: std::optional<std::string> foo(); std::string testing() { return *foo(); // No copy/move constructor call }