Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are the days of passing const std::string & as a parameter over?

Tags:

c++

c++11

I heard a recent talk by Herb Sutter who suggested that the reasons to pass std::vector and std::string by const & are largely gone. He suggested that writing a function such as the following is now preferable:

std::string do_something ( std::string inval ) {    std::string return_val;    // ... do stuff ...    return return_val; } 

I understand that the return_val will be an rvalue at the point the function returns and can therefore be returned using move semantics, which are very cheap. However, inval is still much larger than the size of a reference (which is usually implemented as a pointer). This is because a std::string has various components including a pointer into the heap and a member char[] for short string optimization. So it seems to me that passing by reference is still a good idea.

Can anyone explain why Herb might have said this?

like image 596
Benj Avatar asked Apr 19 '12 15:04

Benj


People also ask

What is const std::string?

The data type const string& literally means “a reference to a string object whose contents will not be changed.” There are three ways to pass things around (into and out of functions) in C++: 1. Pass by value - a copy of the original object is created and passed.

What is std :: string& in C++?

C++ has in its definition a way to represent a sequence of characters as an object of the class. This class is called std:: string. String class stores the characters as a sequence of bytes with the functionality of allowing access to the single-byte character.

Is std::string passed by reference?

Passing it in by value is preferable: (std::string) Modifying the string but wanting the caller to see that change. Passing it in by reference is preferable: (std::string &) Sending the string into the function and the caller of the function will never use the string again.

What is passing by const reference?

When you pass by const reference, you take the argument in by reference (avoiding making any copies of it), but cannot make any changes to the original object (much as would happen when you would take the parameters in by value).


2 Answers

The reason Herb said what he said is because of cases like this.

Let's say I have function A which calls function B, which calls function C. And A passes a string through B and into C. A does not know or care about C; all A knows about is B. That is, C is an implementation detail of B.

Let's say that A is defined as follows:

void A() {   B("value"); } 

If B and C take the string by const&, then it looks something like this:

void B(const std::string &str) {   C(str); }  void C(const std::string &str) {   //Do something with `str`. Does not store it. } 

All well and good. You're just passing pointers around, no copying, no moving, everyone's happy. C takes a const& because it doesn't store the string. It simply uses it.

Now, I want to make one simple change: C needs to store the string somewhere.

void C(const std::string &str) {   //Do something with `str`.   m_str = str; } 

Hello, copy constructor and potential memory allocation (ignore the Short String Optimization (SSO)). C++11's move semantics are supposed to make it possible to remove needless copy-constructing, right? And A passes a temporary; there's no reason why C should have to copy the data. It should just abscond with what was given to it.

Except it can't. Because it takes a const&.

If I change C to take its parameter by value, that just causes B to do the copy into that parameter; I gain nothing.

So if I had just passed str by value through all of the functions, relying on std::move to shuffle the data around, we wouldn't have this problem. If someone wants to hold on to it, they can. If they don't, oh well.

Is it more expensive? Yes; moving into a value is more expensive than using references. Is it less expensive than the copy? Not for small strings with SSO. Is it worth doing?

It depends on your use case. How much do you hate memory allocations?

like image 63
Nicol Bolas Avatar answered Sep 18 '22 15:09

Nicol Bolas


Are the days of passing const std::string & as a parameter over?

No. Many people take this advice (including Dave Abrahams) beyond the domain it applies to, and simplify it to apply to all std::string parameters -- Always passing std::string by value is not a "best practice" for any and all arbitrary parameters and applications because the optimizations these talks/articles focus on apply only to a restricted set of cases.

If you're returning a value, mutating the parameter, or taking the value, then passing by value could save expensive copying and offer syntactical convenience.

As ever, passing by const reference saves much copying when you don't need a copy.

Now to the specific example:

However inval is still quite a lot larger than the size of a reference (which is usually implemented as a pointer). This is because a std::string has various components including a pointer into the heap and a member char[] for short string optimization. So it seems to me that passing by reference is still a good idea. Can anyone explain why Herb might have said this?

If stack size is a concern (and assuming this is not inlined/optimized), return_val + inval > return_val -- IOW, peak stack usage can be reduced by passing by value here (note: oversimplification of ABIs). Meanwhile, passing by const reference can disable the optimizations. The primary reason here is not to avoid stack growth, but to ensure the optimization can be performed where it is applicable.

The days of passing by const reference aren't over -- the rules just more complicated than they once were. If performance is important, you'll be wise to consider how you pass these types, based on the details you use in your implementations.

like image 24
justin Avatar answered Sep 20 '22 15:09

justin