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