So I have a function that needs to take an std::vector as a parameter. I'd like to know the best way to declare the parameter so that the underlying array is not deep-copied, as it could be rather large.
// which should I choose?
void myFunc(std::vector<char>); // 1
void myFunc(std::vector<char>&); // 2
void myFunc(std::vector<char>&&); // 3
void myFunc(std::vector<char>*) // 4
Which should I choose? Also, I won't be modifying the vector in the function so shouldn't I add const? Should I overload the function and have a combination of these?
If you are going to make a copy of it inside the function anyway:
void myFunc(std::vector<char>);
If you just want to read the argument without copying it:
void myFunc(const std::vector<char>&);
If you want to modify the original vector passed to the function:
void myFunc(std::vector<char>&);
If you want to optimize for rvalues or if you want to move the argument into the function:
void myFunc(std::vector<char>&&);
If you need to be able to signify an optional argument passed by reference:
void myFunc(const std::vector<char>*);
If you need to pass an optional argument that you want to modify if non- nullptr
:
void myFunc(std::vector<char>*);
If you don't want to deep copy std::vector
:
move it into your function:
// foo() decalaration void foo(std::vector<int> v); // usage example std::vector<int> v {0, 1, 2, 3}; foo(std::move(v)); // v is moved into foo() and invalid now
You may also return this vector from function in the same manner:
// foo() decalaration std::vector<int> foo(std::vector<int> v) { return v.push_back(4), std::move(v); } // usage example std::vector<int> v {0, 1, 2, 3}; v = foo(std::move(v)); // now v is {0, 1, 2, 3, 4} and no one were deep copied
But note if you don't move it (call foo(v)
instead of foo(std::move(v))
) then it will be deep copied . Under the hood, parameter v
of foo()
is just constructed by move-constructor .
pass it as reference:
// foo() declaration void foo(std::vector<int>& v);
But now we have a problem: which reference and cv-qualifiers ? Well, in general we have 2 types of references and 4 types of cv-qualifiers , altogether 8 declarations:
void foo(std::vector<int>&); void foo(std::vector<int> const&); void foo(std::vector<int> volatile&); void foo(std::vector<int> const volatile&); void foo(std::vector<int>&&); void foo(std::vector<int> const&&); void foo(std::vector<int> volatile&&); void foo(std::vector<int> const volatile&&);
Of course, part of them are useless and should be deleted. But nevertheless too much declarations also known as perfect forwarding problem (actually, there were no rvalue-references when it was a problem so the problem was 2 times smaller).
For example, if you want to modify v
you need 2 functions at least:
void foo(std::vector<int>&); void foo(std::vector<int>&&);
In this case you will be able to call foo()
on lvalue objects:
std::vector<int> v; foo(v);
as well as on temporary:
foo(std::vector<int>{1, 2, 3, 4, 5});
But how to code just one implementation for different reference types and / or cv-qualifiers? Let me introduce universal references :
template<typename Vector> void foo(Vector&& v);
Vector&&
is always a reference type and may be deduced into
std::vector<int>&
if you pass lvalue of type std::vector<int>
into foo()
:
std::vector<int> v; foo(v); // v is lvalue
std::vector<int> const&
if you pass const lvalue of type std::vector<int>
:
std::vector<int> const v; foo(v); // v is const lvalue
std::vector<int>&&
if you pass rvalue:
foo(std::vector<int>{0, 1, 2}); // v is rvalue
etc...
But in this case you have to check acceptance of type Vector
. But that is another story.
And I definitely find no sense to pass pointers instead of references in this case.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.