
on Mon Sep 08 2008, "Niels Dekker - no return address" <noreply-AT-this.is.invalid> wrote:
Andrei Alexandrescu wrote:
JoshuaMaurice (at) gm...com wrote:
As an aside, the way I've generally done it is as follows, which I assume to be equivalent to the two previous examples.
struct T { T& operator= (T const& x) { T(x).swap(*this); return *this; } void swap(T& x); };
That's the politically correct version. It turns out that, when a function needs to make a copy of its argument, it better takes it by value in the first place. That way, if an rvalue is passed in, the compiler can directly fuse the rvalue to the parameter thus saving a copy.
Do you think this is still relevant when operator= is inline, and it's just doing "T(x).swap(*this)"?
Absolutely. The compiler is allowed to elide the implicit copy when the argument is an rvalue if the corresponding parameter is taken by-value. However, when you write T(x) and x is an lvalue (as it is inside the operator= above), the compiler is required to honor your explicit instruction to make a copy of x.
If so, I might consider submitting another ticket regarding boost::function, which still copy-assigns in the "politically correct" way at the moment. As so many of us do :-)
Could you do that for all the other libraries too? I'm serious, I've been meaning to make a sweep across the source code to fix this.
Thanks! Looking a the example of passing the result of MakeUrl() to the function Connect(const String& url), you wrote: "For a compiler to optimize away the copy, it has to do the Herculean job of (1) getting access to Connect's definition (hard with separately compiled modules), (2) parse Connect's definition to develop an understanding of it, and (3) alter Connect's behavior so that the temporary is fused with finalUrl."
Now is it still such a Herculean job for a compiler to do so for an /inline/ assignment operator that's only just swapping a temporary copy? Honestly, I haven't done any profiling on this, so I just hope you did so already :-)
Yes, it's still a fairly herculean job. The compiler would have to prove that the entire program works exactly the same way with or without the copy, proving that nothing about the address or identity of the new T is significant. While it's possible that someone might implement that optimization for some cases where T's copy constructor, swap, and destructor are all inlined, that level of understanding is not typically developed in optimizers. In the case of copy-elision for by-value arguments and return values, the compiler is explicitly allowed to _assume_ there is no semantic difference between the original rvalue and its copy. That's low-hanging fruit for a compiler writer, that pays huge dividends. -- Dave Abrahams BoostPro Computing http://www.boostpro.com