返回一对对象
Returning a pair of objects
以下是反模式:
auto f() {
std::vector<int> v(100000);
return std::move(v); // no need to use std::move thanks to RVO (return value optimization)
}
使用 std::move
甚至会产生最糟糕的代码(参见 here)
但是遇到以下情况怎么办:
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?
}
对于第二个片段,
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 std::make_pair()
函数的结果。那是一个右值。
但是,OP 的问题可能浓缩为 Named Return Value Optimization 是否(或为什么不)在作为 std::pair
返回时仍然适用于 v0
/v1
。
因此,忽略了v0
/v1
不再是return
的主题,而是成为std::make_pair()
的参数。因此,v0
/v1
是左值——如果需要 move-semantic,则必须应用 std::move(v0), std::move(v1)
将它们转换为右值。
#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 NRVO
}
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());
}
输出:
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&&)
是的,在后一种情况下需要移动以避免复制。
不过,这样会更好:
return std::make_pair(
std::vector<int>(100000),
std::vector<int>(100000));
以下是反模式:
auto f() {
std::vector<int> v(100000);
return std::move(v); // no need to use std::move thanks to RVO (return value optimization)
}
使用 std::move
甚至会产生最糟糕的代码(参见 here)
但是遇到以下情况怎么办:
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?
}
对于第二个片段,
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 std::make_pair()
函数的结果。那是一个右值。
但是,OP 的问题可能浓缩为 Named Return Value Optimization 是否(或为什么不)在作为 std::pair
返回时仍然适用于 v0
/v1
。
因此,忽略了v0
/v1
不再是return
的主题,而是成为std::make_pair()
的参数。因此,v0
/v1
是左值——如果需要 move-semantic,则必须应用 std::move(v0), std::move(v1)
将它们转换为右值。
#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 NRVO
}
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());
}
输出:
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&&)
是的,在后一种情况下需要移动以避免复制。
不过,这样会更好:
return std::make_pair(
std::vector<int>(100000),
std::vector<int>(100000));