The following is an anti-pattern:
auto f() {
std::vector<int> v(100000);
return std::move(v); // no need to use std::move thanks to RVO (return value optimization)
}
Using a std::move
can even produce worst code (see here)
However, what should I do in the following situation:
auto f() {
std::vector<int> v0(100000);
std::vector<int> v1(100000);
return std::make_pair(std::move(v0),std::move(v1)); // is the move needed?
}
CodePudding user response:
Yes, the move is needed to avoid copy in the latter case.
However, this would be even better:
return std::make_pair(
std::vector<int>(100000),
std::vector<int>(100000));
CodePudding user response:
For the second snippet,
auto f() {
std::vector<int> v0(100000);
std::vector<int> v1(100000);
return std::make_pair(std::move(v0),std::move(v1)); // is the move needed?
}
return
returns the result of the std::make_pair()
function. That's an RValue.
However, the OP's question probably condenses to whether (or why not) Named Return Value Optimization still applies to v0
/v1
when returned as a std::pair
.
Thereby, it's overlooked that v0
/v1
aren't subject of return
anymore, but become arguments of std::make_pair()
. As such, v0
/v1
are LValues – std::move(v0), std::move(v1)
have to be applied to turn them into RValues if move-semantic is intended.
#include <iostream>
template <typename T>
struct Vector {
Vector(size_t n)
{
std::cout << "Vector::Vector(" << n << ")\n";
}
Vector(const Vector&)
{
std::cout << "Vector::Vector(const Vector&)\n";
}
Vector(const Vector&&)
{
std::cout << "Vector::Vector(const Vector&&)\n";
}
};
auto f1() {
Vector<int> v(100000);
return std::move(v); // over-pessimistic
}
auto f2() {
Vector<int> v(100000);
return v; // allows NVRO
}
auto f3() {
Vector<int> v0(100000);
Vector<int> v1(100000);
return std::make_pair(v0, v1); // copy constructor called for v0, v1
}
auto f4() {
Vector<int> v0(100000);
Vector<int> v1(100000);
return std::make_pair(std::move(v0),std::move(v1)); // move constructor called for v0, v1
}
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__
int main()
{
DEBUG(f1());
DEBUG(f2());
DEBUG(f3());
DEBUG(f4());
}
Output:
f1();
Vector::Vector(100000)
Vector::Vector(const Vector&&)
f2();
Vector::Vector(100000)
f3();
Vector::Vector(100000)
Vector::Vector(100000)
Vector::Vector(const Vector&)
Vector::Vector(const Vector&)
f4();
Vector::Vector(100000)
Vector::Vector(100000)
Vector::Vector(const Vector&&)
Vector::Vector(const Vector&&)