1

Consider this code:

void doSomethingWithString(string& mString) { // mString gets modified in here } string getCopy1(const string& mString) { string result{mString}; doSomethingWithString(result); return result; } string getCopy2(string mString) { doSomethingWithString(mString); return mString; } 

Between getCopy1 and getCopy2, what one:

  • Clearly shows that the passed string isn't going to get modified
  • Clearly shows that the user will get a new string returned
  • Is faster / can be better optimized by the compiler (C++11 enabled, consider move semantics)

?

7
  • 1
    Not sure what the question is. Don’t you forego the answer? Commented Jun 10, 2013 at 13:03
  • @KonradRudolph The questions are the three listed points. Basically, both getCopy1 and getCopy2 do the same thing - what one is faster and more expressive? Commented Jun 10, 2013 at 13:05
  • Shameless plug: see stackoverflow.com/questions/16944715/… -- I hesitate to mark the question as duplicate because the other one is specifically about constructors (and initializing member variables from a parameter) and there are other minor differences but the big picture is the same. Commented Jun 10, 2013 at 13:05
  • 4
    If you're making a copy in any case, do it in the parameter, so getCopy2 it is. Commented Jun 10, 2013 at 13:31
  • 2
    @Vee: No, the pass-by-value is better in your case. See the answers, they explain it. Commented Jun 10, 2013 at 13:40

4 Answers 4

4

Either version clearly shows there is no intent to modify the value that is passed in.

getCopy2 is more efficient in the case where an rvalue is being passed as the parameter. In that case, no copy needs to be done, since the parameter will be moved instead of copied, and you are doing no internal copying. For getCopy1, you always force at least one copy to be made. If an lvalue is being passed as the parameter, then a move needs to be done instead of creating a reference. Which is more efficient depends on a lot of details of the compiler and the string implementation, but the speed of the move should be comparable to the speed of creating a reference.

I don't see any difference as far as the return value.

Sign up to request clarification or add additional context in comments.

2 Comments

I would suspect that a good optimizer might fix this, but I agree. getCopy2 is better, both stylistically, and more optimizers would handle that case better.
Thanks, I understand now. I improved my library using "getCopy2-style" argument passing where possible. (github.com/SuperV1234/SSVUtils/commit/…)
3

Between getCopy1 and getCopy2, what one:

  • Clearly shows that the passed string isn't going to get modified

Both: first, because although it takes a reference, the reference is const; second, because it creates it's own copy.

  • Clearly shows that the user will get a new string returned

Both: they both return a string instance;

  • Is faster / can be better optimized by the compiler (C++11 enabled, consider move semantics)

The second is better to use: if you are making a copy of the input parameter, better do it in the parameter itself (better let the compiler make the copy, on call); It is also better because in the case of a rvalue reference, there is no extra copy done.

Comments

3

Ran some tests, with G++ 4.8.1 on Linux Mint x64. Flags: -std=c++11 -O3 -DNDEBUG

void doSomethingWithString(string& mString) { mString[0] = 'f'; } string getCopy1(const string& mString) { string result{mString}; doSomethingWithString(result); return result; } string getCopy2(string mString) { doSomethingWithString(mString); return mString; } int main() { string s{"132958fdgefi9obm3890g54"}; string t{""}; { startBenchmark(); for(int i{0}; i < 20000000; ++i) t = getCopy1(s); log(endBenchmark(), "getCopy1 variable"); } { startBenchmark(); for(int i{0}; i < 20000000; ++i) t = getCopy1("abcsd"); log(endBenchmark(), "getCopy1 literal"); } { startBenchmark(); for(int i{0}; i < 20000000; ++i) t = getCopy2(s); log(endBenchmark(), "getCopy2 variable"); } { startBenchmark(); for(int i{0}; i < 20000000; ++i) t = getCopy2("abcsd"); log(endBenchmark(), "getCopy2 literal"); } return 0; } 

Output:

[getCopy1 variable] 1236 ms [getCopy1 literal] 1845 ms [getCopy2 variable] 993 ms [getCopy2 literal] 857 ms 

Conclusion:

getCopy2 is faster, especially with rvalues (literal strings).

Comments

2

getCopy2 can be often better optimized. This is neatly explained in "Want Speed? Pass by Value."

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.