-1

I have a function what creates a vector, does somthing with it and then return it as an rvalue using std::move. When I am assigning it to a variable, I'm expecting to call std::vector's move constructor std::vector::vector(vector&&). But I'm getting a segmentation fault.

Here's my code:

#include <iostream>
#include <vector>

std::vector<int>&& getVector(int start)
{
    auto result = std::vector<int>();
    result.reserve(10);
    for (int i = start; i < start + 10; i++)
        result.push_back(i);
    return std::move(result);
}

int main()
{
    std::cout << "Did I break here 1" << std::endl;
    auto vec = getVector(10);
    std::cout << "Did I break here 2" << std::endl;
}

It gives me

Did I break here 1
[1]    55419 segmentation fault <executable_file>

I just guessing there's someting wrong with the move constructor or the rvalues. Btw I return an rvalue to not copy the vector's value an steal the pointer to it.

Sisyffe
  • 162
  • 11
  • I can't understand how you intend for this to work, or how you intend for the calling code to work. Why not just return by value? – Karl Knechtel Dec 10 '22 at 13:42
  • 3
    You're [returning a reference to a local variable](https://godbolt.org/z/6PferxqE5). Turn on all warnings. [Why should I always enable compiler warnings?](https://stackoverflow.com/questions/57842756/why-should-i-always-enable-compiler-warnings). – Jason Dec 10 '22 at 13:42
  • Returning it by value constructs another vector in the caller's scope and copy the data, right? – Sisyffe Dec 10 '22 at 13:43
  • @JasonLiam I use `-Wall -Wextra -Wpedantic` with clang++. And it's not a reference because it's an rvalue – Sisyffe Dec 10 '22 at 13:43
  • @Sisyffe See gcc give warning [here](https://godbolt.org/z/6PferxqE5) – Jason Dec 10 '22 at 13:44
  • The return type `std::vector&&` is **not** "returning an rvalue". – Eljay Dec 10 '22 at 13:45
  • "Returning it by value constructs another vector in the caller's scope and copy the data, right?" Yes. This is unavoidable, because the local vector **does not exist** after the function returns. That's the **point** of calling functions and having locally scoped variables. (Generally, this is implemented by using the hardware stack; after the function returns, the stack pointer is adjusted, such that the function's local variables are no longer considered accessible, valid memory. In C++, destructors also run as values fall out of scope.) – Karl Knechtel Dec 10 '22 at 13:46

1 Answers1

0

You are returning a reference to the local variable result. That object is destroyed before the function returns and you are then trying to move-construct vec from a dangling reference.

You do not need to return-by-rvalue-reference or to std::move explicitly in a return statement to get move semantics. You should just do the straight-forward thing:

std::vector<int> getVector(int start)
{
    auto result = std::vector<int>();
    //...
    return result;
}

//...

auto vec = getVector(10);

Now vec will be move-constructed from result and the constructor call may even be elided. Before C++17 there may be three (including = std::vector<int>()) move constructor calls if the compiler is not optimizing. Since C++17 there will only be one at most. In any case there will not be any copy constructor calls.

user17732522
  • 53,019
  • 2
  • 56
  • 105
  • So `auto vec = getVector(10)` will automaticly call the move constructor and not the copy one? – Sisyffe Dec 10 '22 at 13:52
  • @Sisyffe If `getVector(10)` is a rvalue expression, then yes. And a function call expression to a function returning a non-reference is a prvalue expression, a type of rvalue expression. – user17732522 Dec 10 '22 at 13:53
  • Ok Thanks. C++ is wierd, doesn't it? – Sisyffe Dec 10 '22 at 13:55
  • @Sisyffe It is designed exactly so that moves happen automatically where a copy can't be needed. There is no way you can use `result` after the `return` or use the function return value before/after the construction of `vec`, so there shouldn't be any need to copy. So the straight-forward code doesn't copy and moves instead and even elides to the best ability of the compiler. – user17732522 Dec 10 '22 at 13:57
  • It was an optimization call RVO before the move semantics exists. But it still requires the return type copyable (a.k.a copy constructor defined and visible). I believe it is still there at least in C++11. – Todd Wong Dec 10 '22 at 15:04
  • @ToddWong It only requires the type to be move-constructible since C++11. RVO from the return value is also mandatory since C++17, so it doesn't require even that, but NVRO (which allows eliding the move construction from `result` in `return result;`) is still non-mandatory, so the move-constructibility is required for that. – user17732522 Dec 10 '22 at 15:13