std::move 和 std::forward 之间的混淆
Confusion between std::move and std::forward
所以我只是为了我的理解写了一个 std::forward
的样本和人为的例子,但它没有按我预期的方式工作。在下面的程序中
#include <string>
#include <iostream>
struct A
{
A(const std::string& m) : m_(m) {};
std::string m_;
};
template<typename T>
void funcB(T&& obj) // universal reference
{
std::string local = std::move(obj.m_); // using std::move on universal reference. Bad.
std::cout << "funcB : " << local << '\n';
}
template<typename T>
void funcC(T&& obj) // universal reference
{
std::string local = std::forward<std::string>(obj.m_); // using std::move on universal reference
std::cout << "funcC : " << local << '\n';
}
template<typename T>
void funcD(T&& obj) // universal reference
{
T local = std::forward<T>(obj); // using std::move on universal reference
std::cout << "funcD : " << local.m_ << '\n';
}
int main()
{
A obj("firstString");
//funcA(obj); // We get compiler error. Rvalue reference cannot be bound to Lvalue
funcB(obj);
std::cout << "Main : " << obj.m_ << '\n';
A obj2("secondString");
funcC(obj2);
std::cout << "Main : " << obj2.m_ << '\n';
A obj3("thirdString");
funcD(obj3);
std::cout << "Main : " << obj3.m_ << '\n';
}
在输出中
funcB : firstString
Main :
funcC : secondString
Main :
funcD : thirdString
Main : thirdString
在funcC
中,即使我使用了通用引用并且这里它绑定到左值,但当我std::forward<std::string>
时字符串被移动了。因此在最后一行,"Main:" 之后没有输出。有人可以请如何移动字符串,即使 obj 绑定到左值。
看了一本书才知道这个问题的答案。
在funcC中,std::forward<std::string>(obj.m_)
相当于移动字符串。
但是在 funcD 中,std::forward
被实例化为 std::forward<struct A&>(obj)
,它被复制了。
在这两种情况下,您都在构造一个字符串 std::string local
,并将右值引用作为参数。因此,local
是 move-constructed 来自该引用的原始对象 referred-to。
这与std::forward
或std::move
没有太大关系;此外,您没有初始化一个引用("binding a reference to an lvalue" 来自 Meyers 的文本;引用将在 LHS 上)——您只是从另一个对象构造一个对象。
然而,如果不写 std::forward
或 std::move
你最终会 复制 那些函数参数,因为右值引用是放在 local
声明的初始化端。
因此,在这种情况下,std::forward
和 std::move
具有相同的效果。但是,它们不是一回事,通常不应视为可互换。阅读以下内容了解更多信息:
- What's the difference between std::move and std::forward
所以我只是为了我的理解写了一个 std::forward
的样本和人为的例子,但它没有按我预期的方式工作。在下面的程序中
#include <string>
#include <iostream>
struct A
{
A(const std::string& m) : m_(m) {};
std::string m_;
};
template<typename T>
void funcB(T&& obj) // universal reference
{
std::string local = std::move(obj.m_); // using std::move on universal reference. Bad.
std::cout << "funcB : " << local << '\n';
}
template<typename T>
void funcC(T&& obj) // universal reference
{
std::string local = std::forward<std::string>(obj.m_); // using std::move on universal reference
std::cout << "funcC : " << local << '\n';
}
template<typename T>
void funcD(T&& obj) // universal reference
{
T local = std::forward<T>(obj); // using std::move on universal reference
std::cout << "funcD : " << local.m_ << '\n';
}
int main()
{
A obj("firstString");
//funcA(obj); // We get compiler error. Rvalue reference cannot be bound to Lvalue
funcB(obj);
std::cout << "Main : " << obj.m_ << '\n';
A obj2("secondString");
funcC(obj2);
std::cout << "Main : " << obj2.m_ << '\n';
A obj3("thirdString");
funcD(obj3);
std::cout << "Main : " << obj3.m_ << '\n';
}
在输出中
funcB : firstString
Main :
funcC : secondString
Main :
funcD : thirdString
Main : thirdString
在funcC
中,即使我使用了通用引用并且这里它绑定到左值,但当我std::forward<std::string>
时字符串被移动了。因此在最后一行,"Main:" 之后没有输出。有人可以请如何移动字符串,即使 obj 绑定到左值。
看了一本书才知道这个问题的答案。
在funcC中,std::forward<std::string>(obj.m_)
相当于移动字符串。
但是在 funcD 中,std::forward
被实例化为 std::forward<struct A&>(obj)
,它被复制了。
在这两种情况下,您都在构造一个字符串 std::string local
,并将右值引用作为参数。因此,local
是 move-constructed 来自该引用的原始对象 referred-to。
这与std::forward
或std::move
没有太大关系;此外,您没有初始化一个引用("binding a reference to an lvalue" 来自 Meyers 的文本;引用将在 LHS 上)——您只是从另一个对象构造一个对象。
然而,如果不写 std::forward
或 std::move
你最终会 复制 那些函数参数,因为右值引用是放在 local
声明的初始化端。
因此,在这种情况下,std::forward
和 std::move
具有相同的效果。但是,它们不是一回事,通常不应视为可互换。阅读以下内容了解更多信息:
- What's the difference between std::move and std::forward