std::move和std::forward只是一种强制类型转换¶
std::move 不完全符合标准的实现如下:
template<typename T>
decltype(auto) move(T&& x)
{
using ReturnType = remove_reference_t<T>&&;
return static_cast<ReturnType>(x);
}
std::move 会保留 cv 限定符:
void f(int&&)
{
std::cout << 1 << "\n";
}
void f(const int&)
{
std::cout << 2 << "\n";
}
const int i = 1;
f(std::move(i)); //@ 2
这可能导致的一个问题是,传入右值却执行拷贝操作:
class A {
public:
explicit A(const std::string x)
: s(std::move(x)) {} //@ 转为const std::string&&,调用std::string(const std::string&)
private:
std::string s;
};
因此如果希望移动 std::move 生成的值,传给 std::move 的就不要是 const。
class A {
public:
explicit A(std::string x)
: s(std::move(x)) {} //@ 转为std::string&&,调用std::string(std::string&&)
private:
std::string s;
};
C++11 之前的转发很简单:
void f(int&) { std::cout << 1 << "\n"; }
void f(const int&) { std::cout << 2 << "\n"; }
//@ 用多个重载转发给对应版本比较繁琐
void g(int& x)
{
f(x);
}
void g(const int& x)
{
f(x);
}
//@ 同样的功能可以用一个模板替代
template<typename T>
void h(T& x)
{
f(x);
}
int main()
{
int a = 1;
const int b = 1;
g(a); h(a); //@ 11
g(b); h(b); //@ 22
g(1); //@ 2
h(1); //@ 错误
}
C++11 引入了右值引用,但原有的模板无法转发右值。如果使用 std::move 则无法转发左值,因此为了方便引入了 std::forward 。
void f(int&) { std::cout << 1 << "\n"; }
void f(const int&) { std::cout << 2 << "\n"; }
void f(int&&) { std::cout << 3 << "\n"; }
//@ 用多个重载转发给对应版本比较繁琐
void g(int& x)
{
f(x);
}
void g(const int& x)
{
f(x);
}
void g(int&& x)
{
f(std::move(x));
}
//@ 同样可以用一个模板来替代上述功能
template<typename T>
void h(T&& x)
{
f(std::forward<T>(x)); //@ 注意std::forward的模板参数是T
}
int main()
{
int a = 1;
const int b = 1;
g(a); h(a); //@ 11
g(b); h(b); //@ 22
g(std::move(a)); h(std::move(a)); //@ 33
g(1); h(1); //@ 33
}
看起来完全可以用 std::forward 取代 std::move,但std::move 的优势在于清晰简单。
h(std::forward<int>(a)); //@ 3
h(std::move(a)); //@ 3
结合可变参数模板,完美转发可以转发任意数量的实参:
template<typename... Ts>
void f(Ts&&... args)
{
g(std::forward<Ts>(args)...); //@ 把任意数量的实参转发给g
}