Elimination of temporary situations during operator overload

Note: as indicated by sellibitze. I am not updating the rvalues ​​links, so the methods I suggest contain errors, read it anwser to understand which one.

Yesterday I read one of Linus’s request , and there is (somewhere) rant about operator overloading.

The complaint seems that if you have an object of type S , then:

 S a = b + c + d + e; 

may take a long time.

In C ++ 03, we have a copy of elision to prevent this:

 S a = ((b + c) + d) + e; 

I would hope that the last ... + e will be optimized, but I wonder how many time times are created using custom operator+ .

Someone in the thread suggested using Expression templates to solve the problem.

Now this flow goes back to 2007, but now, when we think about eliminating time series, we think Move .

So, I was thinking about a set of overload operators that we should write in order not to eliminate time constraints, but to limit the cost of their construction (resource theft).

 S&& operator+(S&& lhs, S const& rhs) { return lhs += rhs; } S&& operator+(S const& lhs, S&& rhs) { return rhs += lhs; } // * S&& operator+(S&& lhs, S&& rhs) { return lhs += rhs; } 

Is this set of operators sufficient? Is it generalizable (in your opinion)?

*: this implementation assumes commutativity, it does not work for the infamous string .

+4
source share
1 answer

If you're thinking of a custom, move-supported string class, the right way to use each combination of argument value categories is:

 S operator+(S const& lhs, S const& rhs); S operator+(S && lhs, S const& rhs); S operator+(S const& lhs, S && rhs); S operator+(S && lhs, S && rhs); 

Functions return prvalue instead of xvalue. The return value of x is usually a very dangerous thing. - std :: move and std :: forward are obvious exceptions. If you returned the link to rvalue, you would break the code, for example:

 for (char c : my_string + other_string) { //... } 

This loop behaves (according to 6.5.4 / 1 in N3092), as if the code:

 auto&& range = my_string + other_string; 

This, in turn, leads to a deceived link. The temporary object does not expire because your + operator does not return a prvalue value. Returning objects by value is excellent. This will create temporary objects, but these objects have rvalues, so we can steal their resources to make them very efficient.

Secondly, your code also should not compile for the same reason that it will not compile:

 int&& foo(int&& x) { return x; } 

Inside the body of the function x, there is an lvalue, and you cannot initialize the "return value" (in this case, the rvalue reference) with the expression lvalue. So, you will need an explicit listing.

Third, you are missing const & + const & overload. If both arguments are equal to lvalues, the compiler will not find a suitable + operator in your case.

If you do not want so many overloads, you can also write:

 S operator+(S value, S const& x) { value += x; return value; } 

I intentionally did not write return value+=x; , because this statement probably returns an lvalue reference that would lead to a copy of the return value construct. With the two lines that I wrote, the return value will move through the value.

 S x = a + b + c + d; 

At least this case is very effective because there is no need for unnecessary copying, even if the compiler cannot exclude copies - thanks to the line class with support for moving. In fact, with a class like std :: string, you can use the swap quick exchange function and make it efficient in C ++ 03 if you have a reasonably smart compiler (like GCC):

 S operator+(S value, S const& x) // pass-by-value to exploit copy elisions { S result; result.swap(value); result += x; return result; // NRVO applicable } 

See David Abraham's article Want speed? Pass by value . But these simple operators will not be so efficient:

 S x = a + (b + (c + d)); 

Here the left side of the operator is always the value of l. Since the + operator takes its left side by value, this leads to many copies. Four overloads from above also do a great job with this example.

It has been some time since I read Linus' old story. If he complained about unnecessary copies in relation to std :: string, this complaint is no longer valid in C ++ 0x, but it was almost invalid before. You can efficiently concatenate many lines in C ++ 03:

 S result = a; result += b; result += c; result += d; 

But in C ++ 0x, you can also use operator + and std :: move. It will also be very effective.

I really looked at the Git source code and its string management (strbuf.h). It looks well thought out. With the exception of the disconnect / attach function, you get the same thing with the std :: string movement turned on with the obvious advantage that the resource that it automatically controls the class itself, as opposed to the user who needs to remember to call the correct functions on (strbuf_init , strbuf_release).

+4
source

All Articles