I've seen the following code in our project and asked myself what are the technical and mental implications:
class A {
public:
A(const A&);
A(A &&);
~A();
A &operator += (const A &);
A operator + (const A &);
private:
class B;
B *b;
};
A factory();
void sink(const A &);
void foo(const A &x) {
sink(factory() += x); // <--
}
In the highlighted line I expected sink(factory() + x). I remarked that in the review process and got the reply that the current implementation is more efficient since no second temporary object is created. As a self respecting nerd I immediately fact checked this statement here and got the following x86_64 assembly output.
foo(A const&):
push rbx
mov rbx, rdi
sub rsp, 16
lea rdi, [rsp+8]
call factory()
mov rsi, rbx
lea rdi, [rsp+8]
call A::operator+=(A const&)
mov rdi, rax
call sink(A const&)
lea rdi, [rsp+8]
call A::~A()
add rsp, 16
pop rbx
ret
Versus
bar(A const&):
push rbx
mov rbx, rdi
sub rsp, 16
mov rdi, rsp
call factory()
mov rdx, rbx
mov rsi, rsp
lea rdi, [rsp+8]
call A::operator+(A const&)
lea rdi, [rsp+8]
call sink(A const&)
lea rdi, [rsp+8]
call A::~A() [complete object destructor]
mov rdi, rsp
call A::~A() [complete object destructor]
add rsp, 16
pop rbx
ret
You obviously save one temporary object! But intuitively I still would prefer the plus operator and would've hoped that the compiler is able to optimize the temporary objects away by constructing those objects in place such that no second temporary object must be destructed.
What is your take on this "optimization"? Are there other pro and counter arguments? Is there a possible way to save the plus operator approach? I always thought that move semantics will handle such things, it will prevent all those temporary objects, saves us from the proxy objects from the past, lets us write more direct and functional code and much much more...
The compiler doesn't know how
Ais implemented so it can't know if any of the constructors, destructors or operators have side effects so it can't prove that optimising them away fulfils the "as if" rules, it therefore has to leave your code exactly as written.