ChatGPT解决这个技术问题 Extra ChatGPT

Why is it OK to return a 'vector' from a function?

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;
}
It get copied when return.
No one guarantees.. It will die, but after it's copied.
You only have a problem if your function returns a reference : std::vector<std::string>&
@songyuanyao no, it will be moved.
@songyuanyao Yes. C++11 is the current standard, so C++11 is C++.

P
Peter Mortensen

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.


It will be moved, not copied. This is guaranteed.
Does this apply for C++10 as well?
There is no such thing as C++10.
C++03 had no move semantics (but copy may have been elided, though), but C++ is C++11 and the question was about C++.
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.
π
πάντα ῥεῖ

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).


But is the efficient or have any performance concerns say for vector which may hold 1000 entries?
@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).
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 :)
@zadane "I am afraid they will mark it duplicate of this" Could well be. Just have a look at the higher voted answer. Even for older implementations you shouldn't worry, those will be mostly optimized correctly by those compilers anyway.
M
Mats Petersson

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.


C
Caduchon

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


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.)
@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. :-)
P
Peter Mortensen

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


P
Peter Mortensen

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.


..........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 successes 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.