3

Here is my confusion. I think s1 and s2 are function local object instance of type string, we should not return function local scope object to outside. But it seems below code works, wondering why?

vector<string> testString(){
    vector<string> result;
    string s1 = "123";
    string s2 = "456";
    result.push_back(s1);
    result.push_back(s2);

    return result;
}

void testStringWrapper(){
    vector<string> result = testString();
    for (int i=0; i<result.size(); i++) {
        cout << result[i] << endl;
    }
}
gsamaras
  • 71,951
  • 46
  • 188
  • 305
Lin Ma
  • 9,739
  • 32
  • 105
  • 175
  • 1
    You are free to copy (or logically copy) objects. Returning a *reference* or *pointer* to a local variable is something else. – Cheers and hth. - Alf Sep 16 '17 at 04:38
  • 1
    Your code doesn't return `s1` or `s2`. What do you feel is a problem here? – Igor Tandetnik Sep 16 '17 at 04:38
  • @IgorTandetnik, I think when we call push_back, we make a copy of `s1` and `s2`, but `result` defined in `testString ` is local object, it is ok to be used outside in `testStringWrapper `? – Lin Ma Sep 16 '17 at 04:39
  • 3
    The function returns a copy of `result`, not `result` itself. – Igor Tandetnik Sep 16 '17 at 04:40
  • @Cheersandhth.-Alf, thanks. My concern is, if C++ runtime clears local object, is there an issue? – Lin Ma Sep 16 '17 at 04:40
  • 1
    Even though `result` is local, when you return, you return a copy of `result`, or a "moved" version of `result` and everything inside it. See the discussion here: https://stackoverflow.com/questions/4986673/c11-rvalues-and-move-semantics-confusion-return-statement – TuanDT Sep 16 '17 at 04:41
  • @IgorTandetnik, I see, and copy of vector triggers another full copy string object? – Lin Ma Sep 16 '17 at 04:41
  • 1
    Well, here `result` is moved, not copied - essentially, a new `vector` is created and takes over the internal data previously managed by `result`, leaving it empty. So strings are actually copied only once (and you can avoid that copy by writing `result.push_back(std::move(s1));` ) – Igor Tandetnik Sep 16 '17 at 04:43
  • Possible duplicate of [Why it is OK to return vector from function?](https://stackoverflow.com/questions/22655059/why-it-is-ok-to-return-vector-from-function) – user0042 Sep 16 '17 at 06:43

3 Answers3

4

You return nothing by reference or pointer, you return by value -- which is acceptable. Copies/moves are performed as required.

vector<string> testString(){
    // Declares a vector that will be returned:
    vector<string> result;

    // Creates two local strings:
    string s1 = "123";
    string s2 = "456";

    // *Copies* the local strings into the vector *by value*:
    result.push_back(s1);
    result.push_back(s2);

    // Returns the vector *by value*.  A copy might be made, but NVRO will elide it.
    return result;
}

The vector<string> contains copies of the strings from the function locals.

This can be made more efficient by moving the strings into the vector, which "pilfers" the data pointer from the source string object, leaving it in an unspecified-but-valid state:

// Instead of:
// result.push_back(s1);
// result.push_back(s2);

// Move the strings:
result.emplace_back(std::move(s1));
result.emplace_back(std::move(s2));

Then no copy is made, and s1 and s2 no longer contain their respective values -- the vector stole the contents.

cdhowie
  • 158,093
  • 24
  • 286
  • 300
1

You're returning values, so they're copied or moved over to the call site. That is perfectly fine.

user835611
  • 2,309
  • 2
  • 21
  • 29
0

When you do:

vector<string> testString(){
    vector<string> result;
    ...
    return result;
}

you create a vector result at local scope, but just before the function gets out of scope, you return it and the critical part is that the return type of your function is vector<string>, which copies result and returns it to the caller of the function, which assigned it to a brand new value, in your case:

 vector<string> result = testString();

but don't be confused by the same name of result in testString() scope and in testStringWrapper() scope.

These two result vector are different objects in memory. It would be equivalent with having this for example vector<string> foo = testString();.


we should not return function local scope object to outside

True in some cases.

This applies when returning by reference or a pointer. In that case, you would have something like this:

// BAD PRACTICE
vector<string>& testString(){
    vector<string> result;
    ...
    return result;
}

and you would return a reference to an object that would go out of scope as soon as the function terminated. So when you would try to access it outside of testString(), you would access memory that has gone out of scope.

A typical solution to this problem is to dynamically allocate memory for your object with new, return it by reference and then manage the memory yourself (with that said, the memory will not be de-allocated until you call delete on it).

gsamaras
  • 71,951
  • 46
  • 188
  • 305