Are arguments passed through `&&` useful for non-constructor functions?

You can have a function void setData(std::string arg); and call it via setData(std::move(data)); by calling the constructor move, and it will do the same for void setData(std::string && arg); (except that he will be forced to move data into it). The cant compiler will decide if using relocation should be done for simple cases, does it already?

So my question is: use && not only for compilers, but also for general code (for example, for API members created for other developers)?

+6
source share
2 answers

Optimization for r-values

Comparison of void setData(std::string arg) and void setData(std::string&& arg) . In the first case, I assume that setData moves data in place

 class Widget { std::string data; public: void setData(std::string data) { this->data = std::move(data); } }; 

And if we call it that

 w.setData(std::move(data)); 

we get one call to the move constructor to build the function argument and one call to the move assignment operator to move the data to the member variable. So, just two steps.

But if we overload the r-value references, for example:

 class Widget { std::string data; public: void setData(std::string&& data) { this->data = std::move(data); } }; 

You get only one call forwarding assignment operator. You could say, "But traffic is cheap!" what could be true. In some cases, they are not cheap (for example, std::array ), and in the case of std::string most compilers implement small string optimization (SSO), so for small strings moving is not cheaper than a copy.

Optimization for l-values

The argument for passing by value often is that you can optimize for both l-values ​​and r-values ​​without having to provide two overloads void setData(const std::string& arg) and void setData(std::string&& arg) So let's compare what happens if we pass the l-value to void setData(std::string arg) vs void setData(const std::string& arg) . In the first case, you will receive one unconditional copy, and then one move. In the second case, you get only one job. The additional transfer destination is probably small, but it is possible that an unconditional copy is much more expensive than the destination. If you call setData more than once on the same object, the assignment may reuse the existing capacity and avoid re-allocation. An unconditional copy will always perform the selection.

Perhaps these considerations are insignificant in practice, but you should be aware of them.

Documentation / Securing Transfer of Ownership

Another use of r-value reference parameters is to document / enforce ownership. Let's say you had a large object that you can copy and move, then you can only provide void setData(BigData&& data) for forced movement. In addition to making it less likely that someone accidentally makes a copy, it also documents that we take responsibility for the data. You do not need to move the entire object as well, you can just steal part of the object.

+3
source

The Cant compiler will decide whether to use relocation for simple cases, does it already do this?

Even when using void setData(std::string arg); the compiler automatically moves values ​​such as temporary:

 x.setData(my_string + "more"); // move from temporary std::string x.setData(get_a_string()); // move returned-by-value string 

As for the utility with && -overload, consider some call code:

 std::string q = get_a_string(); myObj.setData(std::move(q)); q = pick_a_string_to_copy[rand() % 10]; 

Here, if there is only an overload of setData(std::string s) , then the move constructor for s will own q , and any efficient implementation will leave q without a pointer to dynamically allocated free storage. Then setData() supposed to assign to the data member, which will change the free storage buffer to s , and s will delete[] as setData returned. The next q = line will have to allocate a new buffer for the next value (assuming that size() larger than any short-string optimization buffer).

This contrasts with the situation if there is an overload of setData(std::string&& s) , where, ultimately, the buffer q is most likely to be replaced by the data buffer myobj data, so that q still owns dynamic memory, which may be enough. to save the next value that he assigned - saving a little time. On the other hand, the buffer may be larger than necessary, holding the memory longer than necessary.

Simply put, with && caller can process free storage buffers rather than losing a buffer or doing inefficient copying.

All functional differences are not always relevant, but they exist.

+4
source

Source: https://habr.com/ru/post/987472/


All Articles