3

I recently saw the following code-block as a response to this question: Split a string in C++?

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string>     
&elems) {
    std::stringstream ss(s);
    std::string item;
    while(std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}

Why is returning the passed-by-reference array "elems" so important here? Couldn't we make this a void function, or return an integer to indicate success/failure? We are editing the actual array anyway, right?

Thank you!

Community
  • 1
  • 1
Ben D.
  • 133
  • 2
  • 6
  • 6
    It is actually returning the vector by reference, not a memory address. – juanchopanza Jul 18 '12 at 17:56
  • 1
    @Ben Decato FYI: If you look at an implementation of a << or >> operator overload (anywhere) you'll see chaining like this. Those operators take in a stream and return a reference to the same stream so you can do things like std::cout << x << y << z << std::endl; all in a line. – John Humphreys Jul 18 '12 at 18:16
  • Thank you -- I edited the title of the post to reflect this newfound bit of knowledge. – Ben D. Jul 18 '12 at 19:04

2 Answers2

6

By returning a reference to the object you passed in you can do some chaining or cascading in one expression and be working with the same vector the whole time. Some people find this conventient: IE

  std::vector<std::string> elems;
  std::cout << "Number of items:" << split("foo.cat.dog", '.', elems).size();

  // get just foo
  std::cout << "First item is:" << split("foo.cat.dog", '.', elems)[0];

  // change first item to bar
  split("foo.cat.dog", '.', elems)[0] = "bar";
Doug T.
  • 64,223
  • 27
  • 138
  • 202
1

It's not returning the memory address, It's actually returning the object by non-const reference. The same way it was passed in. This might seem a bit of overkill because the calling code can either rely on the third parameter passed, which will be populated on return from the function, or the return parameter.

The reason for doing it this way is to allow chaining. So you can do:

split(myString, ',', asAVector).size().

which will perform the function and allow you to chain the results by calling a function on the vector (in this case size)

Despite the neatness, there are some potential drawbacks to this approach: For example, no error code is present in the return value so you are reliant on the function either working proerly or throwing an exception; therefore you'd usually expect to wrap the above with try / catch semantics. Of course, the more chaining you do, the more likely it will be that the possibilities for different types of exception will go up so you may have to cover more catch blocks.

Mind you, passing back by reference is a whole lot better than passing back by pointer. Chaining with pointers is notorious for crashing when one of the functions in the chain decides to fail and return 0.

Component 10
  • 10,247
  • 7
  • 47
  • 64