115

Please consider this code. I have seen this type of code several times. words is a local vector. How is it possible to return it from a function?

Can we guarantee it will not die?

 std::vector<std::string> read_file(const std::string& path)
 {
    std::ifstream file("E:\\names.txt");

    if (!file.is_open())
    {
        std::cerr << "Unable to open file" << "\n";
        std::exit(-1);
    }

    std::vector<string> words;//this vector will be returned
    std::string token;

    while (std::getline(file, token, ','))
    {
        words.push_back(token);
    }

    return words;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Pranit Kothari
  • 9,721
  • 10
  • 61
  • 137
  • 19
    It get copied when return. – songyuanyao Mar 26 '14 at 08:22
  • 6
    No one guarantees.. It *will* die, but after it's copied. – Maroun Mar 26 '14 at 08:22
  • 7
    You only have a problem if your function returns a reference : `std::vector&` – Caduchon Mar 26 '14 at 08:25
  • 14
    @songyuanyao no, it will be moved. –  Mar 26 '14 at 08:37
  • 1
    @rightfold Only guaranteed in C++11? – songyuanyao Mar 26 '14 at 08:42
  • 15
    @songyuanyao Yes. C++11 is the current standard, so C++11 is C++. –  Mar 26 '14 at 08:42
  • Apparently you should also avoid returning vectors(and other containers) in public .dll's unless you can make sure that the runtime is the same. Mainly for msvc, not sure if gcc has the same problem. – Programmdude Mar 26 '14 at 09:52
  • 1
    @Programmdude Due to issues with member access? I'm guessing that different versions of the MSVC runtime libraries may have some container members in different orders, so even if the containers have the proper members as per the standard, offsets from the start of the object to access the members may be incorrect if a program compiled for one specific runtime is used with another? (Though I'm pretty sure most programs compiled dynamically against the MSVC libraries require the specific DLLs they were linked with anyway, but the same principle applies for two statically linked programs/DLLs.) – JAB Mar 26 '14 at 11:54
  • 3
    possible duplicate of [In C++, is it still bad practice to return a vector from a function?](http://stackoverflow.com/questions/3134831/in-c-is-it-still-bad-practice-to-return-a-vector-from-a-function) – Florian Richoux Mar 26 '14 at 14:38
  • @JAB Yea, apparently its to do with the possibility of different member layouts. I think they work fine being loaded side by side, just not used across the dll boundary. – Programmdude Mar 27 '14 at 01:06
  • 1
    nrvo doesn't happen here? –  Apr 24 '15 at 08:07

6 Answers6

110

Pre C++11:

The function will not return the local variable, but rather a copy of it. Your compiler might however perform an optimization where no actual copy action is made.

See this question & answer for further details.

C++11:

The function will move the value. See this answer for further details.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tim Meyer
  • 12,210
  • 8
  • 64
  • 97
  • 2
    It will be moved, not copied. This is guaranteed. –  Mar 26 '14 at 08:36
  • 1
    Does this apply for C++10 as well? – Tim Meyer Mar 26 '14 at 08:38
  • 28
    There is no such thing as C++10. –  Mar 26 '14 at 08:38
  • C++03 had no move semantics (but copy may have been elided, though), but C++ is C++11 and the question was about C++. –  Mar 26 '14 at 08:39
  • 19
    There is a separate tag for questions exclusive to C++11. Many of us, especially programmers in larger companies are still stuck to compilers which don't fully support C++11 yet. I updated the question to be accurate for both standards. – Tim Meyer Mar 26 '14 at 08:45
  • For C++11, shouldn't you say "The function will move the value, if copy elision doesn't happen". I know that is compiler specific, but that would be a more informative answer. Also the link says the same thing, but many people might not click on that. – rents Oct 20 '16 at 06:56
70

Can we guarantee it will not die?

As long there is no reference returned, it's perfectly fine to do so. words will be moved to the variable receiving the result.

The local variable will go out of scope. after it was moved (or copied).

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • 2
    But is the efficient or have any performance concerns say for vector which may hold 1000 entries? – zar Feb 09 '15 at 20:43
  • @zadane Was this in question? Also I mentioned _moving_ that will avoid to take a copy of the return value actually (available at least with the current standard). – πάντα ῥεῖ Feb 09 '15 at 20:46
  • 2
    No not really in the question but I was looking for answer from that perspective independently. I don't know if I post my question, I am afraid they will mark it duplicate of this :) – zar Feb 09 '15 at 20:51
  • @zadane _"I am afraid they will mark it duplicate of this"_ Could well be. Just have a look at the [higher voted answer](http://stackoverflow.com/a/22655160/1413395). Even for older implementations you shouldn't worry, those will be mostly optimized correctly by those compilers anyway. – πάντα ῥεῖ Feb 09 '15 at 21:09
26

I think you are referring to the problem in C (and C++) that returning an array from a function isn't allowed (or at least won't work as expected) - this is because the array return will (if you write it in the simple form) return a pointer to the actual array on the stack, which is then promptly removed when the function returns.

But in this case, it works, because the std::vector is a class, and classes, like structs, can (and will) be copied to the callers context. [Actually, most compilers will optimise out this particular type of copy using something called "Return Value Optimisation", specifically introduced to avoid copying large objects when they are returned from a function, but that's an optimisation, and from a programmers perspective, it will behave as if the assignment constructor was called for the object]

As long as you don't return a pointer or a reference to something that is within the function returning, you are fine.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
13

To well understand the behaviour, you can run this code:

#include <iostream>

class MyClass
{
  public:
    MyClass() { std::cout << "run constructor MyClass::MyClass()" << std::endl; }
    ~MyClass() { std::cout << "run destructor MyClass::~MyClass()" << std::endl; }
    MyClass(const MyClass& x) { std::cout << "run copy constructor MyClass::MyClass(const MyClass&)" << std::endl; }
    MyClass& operator = (const MyClass& x) { std::cout << "run assignation MyClass::operator=(const MyClass&)" << std::endl; }
};

MyClass my_function()
{
  std::cout << "run my_function()" << std::endl;
  MyClass a;
  std::cout << "my_function is going to return a..." << std::endl;
  return a;
}

int main(int argc, char** argv)
{
  MyClass b = my_function();

  MyClass c;
  c = my_function();

  return 0;
}

The output is the following:

run my_function()
run constructor MyClass::MyClass()
my_function is going to return a...
run constructor MyClass::MyClass()
run my_function()
run constructor MyClass::MyClass()
my_function is going to return a...
run assignation MyClass::operator=(const MyClass&)
run destructor MyClass::~MyClass()
run destructor MyClass::~MyClass()
run destructor MyClass::~MyClass()

Note that this example was provided in C++03 context, it could be improved for C++ >= 11

Caduchon
  • 4,574
  • 4
  • 26
  • 67
  • 1
    This example would be more complete if it included a move constructor and a move assignment operator also, and not just copy constructor and copy assignment operator. (If the move functions aren't present, the copy ones will be used instead.) – Some Guy May 30 '20 at 08:27
  • @SomeGuy I agree, but I don't use C++11. I can't provide knowledge I don't have. I add a note. Feel free to add an answer for C++ >= 11. :-) – Caduchon Jun 02 '20 at 14:27
-5

I do not agree and do not recommend to return a vector:

vector <double> vectorial(vector <double> a, vector <double> b)
{
    vector <double> c{ a[1] * b[2] - b[1] * a[2], -a[0] * b[2] + b[0] * a[2], a[0] * b[1] - b[0] * a[1] };
    return c;
}

This is much faster:

void vectorial(vector <double> a, vector <double> b, vector <double> &c)
{
    c[0] = a[1] * b[2] - b[1] * a[2]; c[1] = -a[0] * b[2] + b[0] * a[2]; c[2] = a[0] * b[1] - b[0] * a[1];
}

I tested on Visual Studio 2017 with the following results in release mode:

8.01 MOPs by reference
5.09 MOPs returning vector

In debug mode, things are much worse:

0.053 MOPS by reference
0.034 MOPs by return vector

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
mathengineer
  • 140
  • 6
-10

This is actually a failure of design. You shouldn't be using a return value for anything not a primitive for anything that is not relatively trivial.

The ideal solution should be implemented through a return parameter with a decision on reference/pointer and the proper use of a "const\'y\'ness" as a descriptor.

On top of this, you should realise that the label on an array in C and C++ is effectively a pointer and its subscription are effectively an offset or an addition symbol.

So the label or ptr array_ptr === array label thus returning foo[offset] is really saying return element at memory pointer location foo + offset of type return type.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 5
    ..........what. It seems clear that you're not qualified to throw around accusations like "failure of design". And in fact, the promotion of value semantics by RVO and move operations is one of _the_ main **success**es of modern C++ style. But you seem to be stuck thinking about raw arrays and pointers, so I wouldn't expect you to grasp that. – underscore_d Sep 10 '16 at 20:01