std::move和之间的区别std::forward是众所周知的,我们使用后者来保留转发对象的值类别,并使用前者转换为右值引用以启用移动语义.
在有效的现代C++中,存在一种指导原则
std::move在rvalue引用std::forward上使用,在通用引用上.
但在以下场景中(以及我们不想更改值类别的场景),
template <class T>
void f(vector<T>&& a)
{
some_func(std::move(a));
}
Run Code Online (Sandbox Code Playgroud)
哪里a不是转发引用而是简单的右值引用,执行以下操作不是完全相同的吗?
template <class T>
void f(vector<T>&& a)
{
some_func(std::forward<decltype(a)>(a));
}
Run Code Online (Sandbox Code Playgroud)
因为这可以很容易地封装在像这样的宏中,
#define FWD(arg) std::forward<decltype(arg)>(arg)
Run Code Online (Sandbox Code Playgroud)
如此总是使用这个宏定义不方便吗?
void f(vector<T>&& a)
{
some_func(FWD(a));
}
Run Code Online (Sandbox Code Playgroud)
写这两种方式不完全相同吗?
呃.值得商榷.在您的特定示例中,它是等效的.但我不会养成习惯.一个原因是因为您希望在转发和移动之间进行语义区分.另一个原因是因为要拥有一个一致的API,你必须拥有MOV它FWD,而且这看起来很糟糕并且什么都不做.但更重要的是,您的代码可能会意外失败.
请考虑以下代码:
#include <iostream>
using namespace std;
#define FWD(arg) std::forward<decltype(arg)>(arg)
struct S {
S() { cout << "S()\n"; }
S(const S&) { cout << "S(const S&)\n"; }
S(S&&) { cout << "S(S&&)\n"; }
};
void some_func(S) {}
void f(S&& s)
{
some_func(FWD(s));
}
int main()
{
f(S{});
}
Run Code Online (Sandbox Code Playgroud)
打印出来
S()
S(S &&)
但是,如果我只是将该FWD行更改为另一对(看似可选)括号,如下所示:
void f(S&& s)
{
some_func(FWD((s)));
}
Run Code Online (Sandbox Code Playgroud)
现在我们得到了
S()
S(const S&)
这是因为现在我们正在使用decltype一个表达式,它计算为左值引用.