让T
为任意类型。考虑一个带有const
[左值]引用的函数:
void f(const T &obj);
假设此函数在内部调用另一个函数,该函数具有右值引用重载:
void g(T &&obj);
如果我们将右值传递给f
,则将调用g
的右值引用重载,还是因为它已经“转换” /绑定到{{1 }}左值参考?
类似地,如果const
调用了一个按值接受f
实例的函数,
T
和void h(T obj);
具有移动构造函数(即T
),将调用move构造函数,还是将调用复制构造函数?
总而言之,如果我们想确保在对某个右值调用T(T &&);
时,右值引用传递给它,保持其右值“状态”,那么我们是否必须为{{ 1}}?
答案 0 :(得分:3)
值类别应用于表达式,而不是对象。 obj
中的f
是左值表达式,因此将被视为左值表达式。请注意,obj
中的g
也是左值表达式;如果表达式是对象的名称,则为左值。
您正在谈论的能力正是转发引用存在的原因:这样就可以通过函数调用来保留参数表达式的值类别的复制/移动方面。 f
必须成为template<typename T> void f(T&& t);
形式的模板,并且在将std::forward
传递给g
时必须使用int*&
。
答案 1 :(得分:2)
使用引用的名称作为表达式时,该表达式始终是左值。无论是左值引用还是右值引用。引用是用左值还是右值初始化也没有关系。
int &&x = 1;
f(x); // Here `x` is lvalue.
因此在void f(const T &obj) {...}
中,obj
始终是左值,无论您作为参数传递什么。
还请注意,值类别在编译时确定。由于f
不是模板,因此其中每个表达式的值类别都不能取决于您传递的参数。
因此:
如果我们将右值传递给
f
,将调用g
的右值引用重载
否。
如果
f
调用了一个按值接受T
实例的函数,则void h(T obj);
和T
具有移动构造函数(即T(T &&);
),将移动构造函数称为
否。
总而言之,如果我们想确保在对某个右值调用
f
时,右值引用传递给它,保持其右值“状态”,那么我们是否必须为{{ 1}}?
提供过载是一种选择。请注意,在这种情况下,您必须在右值重载中显式调用f
。
另一个方法是使用转发引用,如Nicol Bolas建议的那样:
std::move
在这里,template <typename T> void f(T &&t)
{
g(std::forward<T>(t));
}
本质上充当“条件std::forward
”。如果传递了右值,它将移动move
,否则不执行任何操作。