简体   繁体   中英

If rvalues did not bind to const references, how would this affect move semantics and perfect forwarding?

In http://www.reddit.com/r/IAmA/comments/1nl9at/i_am_a_member_of_facebooks_hhvm_team_a_c_and_d/ccjm2qs , Andrei Alexandrescu writes:

I think binding rvalues to const references has been the small mistake that caused the rvalue references Hindenburg... It would be a long discussion. Binding rvalues to const& made sense when first introduced (no templates, few subtleties) but in the long term made it virtually impossible to distinguish rvalues from lvalues on the callee side. That in turn forced an overly complex solution (rvalue references) as an expensive fix.

If the choice had not been made to bind rvalues to const references, how would this affect move semantics and perfect forwarding?

I wasn't going to touch this question, considering it an extremely hypothetical question with not much value in answering. But I've changed my mind. There is value in answering this question that I did not previously comprehend.

Consider the common (not in C++, but in computer languages in general) API to split a string into an array of strings based on one or more delimiters. This ideally would return "string views" or "string_refs" into the original string to avoid copying pieces of the original string. A "string view" or "string_ref" is nothing but a pair of iterators into the original string. So something like:

vector<string_ref> split(const string& str, const string& delim);

With it known that vector<string_ref> refers back into str , it would be a bad idea for this function to bind to an rvalue. Once the client gets around to using the result, the reference into the argument would be long gone. The references would be dangling.

Thus it would be a good idea to forbid this function from accepting rvalue arguments for str , even though it is not going to modify this argument.

That being said, we have decades of experience that strongly indicate that this example is not typical. Most of the time, if a function isn't going to modify an argument, it does not matter if that argument is an rvalue or lvalue.

So if we had a clean sheet, it would make sense to make the common case be the easiest one to handle: rvalues can bind to const X& . But for cases like split , we need syntax for disallowing this common default behavior. How 'bout:

vector<string_ref> split(const string& str, const string& delim);
vector<string_ref> split(const string&& str, const string& delim) = delete;

I'm having trouble coming up with anything more elegant than this.

In other words: Usually, with the benefit of 20/20 hindsight, and lack of backwards compatibility constraints, one can do better with a redesign, given a clean sheet. But in this case, I've hypothesized myself a clean sheet, and I'm having trouble coming up with a better design than what we have.

<Disclaimer> I'm biased. </Disclaimer>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM