Passing vector with std::move function signature
Asked Answered
R

3

9

Consider the code below

void foo(std::vector<int> v)
{
   //do something here
}
//calling the function
vector<int> v1 = {1,2,3};
foo(std::move(v1));

My question is, isn't the function foo supposed to have signature void foo(std::vector<int>&& v) for it to be able to take the r value reference of the vector?

Respiration answered 20/1, 2020 at 20:52 Comment(0)
B
10

My question is, isnt the function foo supposed to have signature void foo(std::vector<int>&& v) for it to be able to take the r value reference of the vector?

If that's what you want, then yes, but that doesn't mean what you have is incorrect. When you pass something to a function it copy initializes the parameter from the source. That means if you do

vector<int> v1 = {1,2,3};
foo(v1);

then foo gets a copy of v1. With

vector<int> v1 = {1,2,3};
foo(std::move(v1));

We copy initialize v from std::move(v1) and since std::move(v1) is an rvalue reference, the move constructor is chosen for v and v1 gets moved into the function.

So, by taking by value you give the option to the caller to give it a temporary, give it a rvalue reference, which will both move the object into the function, or just let a copy happen. If you had void foo(std::vector<int>&& v) Then you could only pass a temporary or std::move() an lvalue. There would be no way to allow the caller to have a copy made without them making one themselves and then moving that copy into the function.

Baudekin answered 20/1, 2020 at 21:0 Comment(4)
Thanks for the answer, but I still dont understand the sequencing of function calls (in terms of when is the move constructor invoked exactly). What exactly does it mean to "copy initialize v from std::move(v1) and ..."Respiration
I mean, given the vector class also has a copy constructor (with const vector<int>&) as signature, then when I call the foo(move(v1)), then move(v1) will be a vector<int>&&, but even the rvalue reference is a lvalue reference, then why wouldnt the copy constructor get called?Respiration
@Respiration std::move is just a function that casts what you give it into a rvalue reference. You then pass that rvalue reference to foo and the compiler will use it to initialize v. Since it's an rvalue reference, the type matches vectors move constructors so the move constructor will be called instead of the copy constructor. Make sense?Baudekin
Yes, makes sense. I think to boil it down, the compiler needs to initialise v, and to do that, it needs to either use copy or move constructor depending on what I pass. I could very well have asked the question that howcome a copy constructor is called when I pass foo(v1), and the answer is that the compiler invoked copy contructor based on the input I provide. :) thanks.Respiration
S
3
void foo(std::vector<int> v)

Means that the function takes a std::vector<int> object as a parameter. It's up to the caller to construct this parameter object however it wants, but then it's owned and used by foo.

In the caller, you have:

foo(/**compiler constructs the v parameter from whatever is here**/);

Therefore:

vector<int> v1 = {1,2,3};
foo(v1); // This constructs the parameter `v` as a copy of `v1`.

Compared to

vector<int> v1 = {1,2,3};
foo(std::move(v1)); // This constructs the parameter `v` by moving from the value of `v1`.

Either is valid, but they do subtly different things.


void foo(std::vector<int>&& v)

Means that the function takes a reference as a parameter, that refers to a std::vector<int> that is elsewhere, and also means that the caller should not use the parameter after this function is done with it, as the caller intends to invalidate it in some way.

The language helps prevent mistakes here by either

  • forcing you to either pass a temporary (so the caller can't accidentally try to use the value of the referenced vector after foo is complete),
  • or forcing you to call std::move, to promise the compiler that you won't try to use the value of the referenced vector after foo is complete)
Sunsunbaked answered 20/1, 2020 at 21:12 Comment(0)
L
2

isn't the function foo supposed to have signature void foo(std::vector<int>&& v) for it to be able to take the r value reference of the vector?

It is std::vector<int>'s move constructor the one with such a signature.

The function foo() takes as argument an std::vector<int> object by value:

void foo(std::vector<int> v) {
   // ...
}

The parameter object v has to be constructed somehow. The argument passed to foo() is used to construct this parameter object.

By calling foo() as in the expression foo(std::move(v1)), the parameter object in foo(), v, is move constructed. This is in contrast with calling foo() as in the expression foo(v1), which results in the parameter object v being copy constructed.

It is therefore the parameter object's move constructor (i.e., v, an std::vector<int> object) the one taking std::vector<int>&&.

Leola answered 20/1, 2020 at 21:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.