先简短回答:通过 const& 调用总是要花费一份副本。 根据条件按价值调用可能只需要一步操作。但这取决于(请查看下面的代码示例以了解此表所指的场景):
lvalue rvalue unused lvalue unused rvalue
------------------------------------------------------
const& copy copy - -
rvalue&& - move - -
value copy, move move copy -
T&& copy move - -
overload copy move - -
所以我的执行摘要是,如果
- 移动很便宜,因为可能会有额外的移动
- 无条件使用该参数。如果不使用参数,按值调用也会花费一份副本,例如因为 if 子句或某事。
按值调用
考虑一个用于复制其参数的函数
class Dog {
public:
void name_it(const std::string& newName) { names.push_back(newName); }
private:
std::vector<std::string> names;
};
如果将左值传递给name_it,则在右值的情况下您也将进行两次复制操作。这很糟糕,因为右值可以让我移动。
一种可能的解决方案是为右值编写重载:
class Dog {
public:
void name_it(const std::string& newName) { names.push_back(newName); }
void name_it(std::string&& newName) { names.push_back(std::move(newName)); }
private:
std::vector<std::string> names;
};
这解决了问题,一切都很好,尽管你有两个代码,两个函数的代码完全相同。
另一种可行的解决方案是使用完美转发,但这也有几个缺点,(例如,完美转发函数非常贪婪,并且使现有的重载 const& 函数无用,通常它们需要在头文件中,他们在目标代码中创建了几个函数等等。)
class Dog {
public:
template<typename T>
void name_it(T&& in_name) { names.push_back(std::forward<T>(in_name)); }
private:
std::vector<std::string> names;
};
另一种解决方案是使用按值调用:
class Dog {
public:
void name_it(std::string newName) { names.push_back(std::move(newName)); }
private:
std::vector<std::string> names;
};
重要的是,正如您提到的std::move。这样,您将拥有一个用于右值和左值的函数。您将移动右值,但接受左值的额外移动,这可能很好如果移动便宜并且无论条件如何都复制或移动参数。
所以最后我真的认为推荐一种方法而不是其他方法是完全错误的。这在很大程度上取决于。
#include <vector>
#include <iostream>
#include <utility>
using std::cout;
class foo{
public:
//constructor
foo() {}
foo(const foo&) { cout << "\tcopy\n" ; }
foo(foo&&) { cout << "\tmove\n" ; }
};
class VDog {
public:
VDog(foo name) : _name(std::move(name)) {}
private:
foo _name;
};
class RRDog {
public:
RRDog(foo&& name) : _name(std::move(name)) {}
private:
foo _name;
};
class CRDog {
public:
CRDog(const foo& name) : _name(name) {}
private:
foo _name;
};
class PFDog {
public:
template <typename T>
PFDog(T&& name) : _name(std::forward<T>(name)) {}
private:
foo _name;
};
//
volatile int s=0;
class Dog {
public:
void name_it_cr(const foo& in_name) { names.push_back(in_name); }
void name_it_rr(foo&& in_name) { names.push_back(std::move(in_name));}
void name_it_v(foo in_name) { names.push_back(std::move(in_name)); }
template<typename T>
void name_it_ur(T&& in_name) { names.push_back(std::forward<T>(in_name)); }
private:
std::vector<foo> names;
};
int main()
{
std::cout << "--- const& ---\n";
{
Dog a,b;
foo my_foo;
std::cout << "lvalue:";
a.name_it_cr(my_foo);
std::cout << "rvalue:";
b.name_it_cr(foo());
}
std::cout << "--- rvalue&& ---\n";
{
Dog a,b;
foo my_foo;
std::cout << "lvalue: -\n";
std::cout << "rvalue:";
a.name_it_rr(foo());
}
std::cout << "--- value ---\n";
{
Dog a,b;
foo my_foo;
std::cout << "lvalue:";
a.name_it_v(my_foo);
std::cout << "rvalue:";
b.name_it_v(foo());
}
std::cout << "--- T&&--\n";
{
Dog a,b;
foo my_foo;
std::cout << "lvalue:";
a.name_it_ur(my_foo);
std::cout << "rvalue:";
b.name_it_ur(foo());
}
return 0;
}
输出:
--- const& ---
lvalue: copy
rvalue: copy
--- rvalue&& ---
lvalue: -
rvalue: move
--- value ---
lvalue: copy
move
rvalue: move
--- T&&--
lvalue: copy
rvalue: move