当 const 引用参数绑定到它时,右值是否保留其 "status"?
Does an rvalue keep its "status" when a const reference parameter binds to it?
设T
为任意类型。考虑一个接受 const
[左值] 引用的函数:
void f(const T &obj);
假设此函数在内部调用另一个具有右值引用重载的函数:
void g(T &&obj);
如果我们将右值传递给 f
,是否会调用 g
的右值引用重载,还是会失败,因为它已被 "converted"/绑定到const
左值引用?
类似地,如果 f
调用了一个按值获取 T
实例的函数,
void h(T obj);
和T
有移动构造函数,(即T(T &&);
),是调用移动构造函数,还是调用复制构造函数?
总而言之,如果我们想确保在对右值调用 f
时,右值引用在传递时保持其右值 "status",我们是否必须提供右值引用f
?
过载
值类别应用于表达式,而不是对象。 f
中的 obj
是左值表达式,因此将被视为左值表达式。请注意 g
中的 obj
是 也是 左值表达式;如果表达式是一个对象的名称,那么它就是一个左值。
您所说的能力正是存在转发引用的原因:因此可以通过函数调用保留参数表达式值类别的 copy/move 方面。 f
必须成为 template<typename T> void f(T&& t);
形式的模板,并且在将其传递给 g
.
时必须使用 std::forward
当您将引用的名称用作表达式时,该表达式始终是左值。不管它是左值引用还是右值引用。引用是用左值还是右值初始化也无关紧要。
int &&x = 1;
f(x); // Here `x` is lvalue.
因此在 void f(const T &obj) {...}
中,obj
始终是一个左值,无论您作为参数传递什么。
另请注意,值类别是在编译时确定的。由于 f
不是模板,其中每个表达式的值类别不能取决于您传递的参数。
因此:
If we pass an rvalue to f
, will the rvalue reference overload of g
be called
没有
if f
called a function that takes an instance of T
by value, void h(T obj);
and T
has a move constructor, (i.e. T(T &&);
), will the move constructor be called
没有
In conclusion, if we wanted to ensure that, when calling f
on an rvalue, the rvalue reference is passed around keeping its rvalue "status", would we necessarily have to provide an rvalue reference overload for f
?
提供重载是一种选择。请注意,在这种情况下,您必须在右值重载中显式调用 std::move
。
另一种选择是使用转发引用,正如 Nicol Bolas 所建议的:
template <typename T> void f(T &&t)
{
g(std::forward<T>(t));
}
在这里,std::forward
本质上充当了 'conditional move
'。如果传递了右值,它会移动 t
,否则什么都不做。
设T
为任意类型。考虑一个接受 const
[左值] 引用的函数:
void f(const T &obj);
假设此函数在内部调用另一个具有右值引用重载的函数:
void g(T &&obj);
如果我们将右值传递给 f
,是否会调用 g
的右值引用重载,还是会失败,因为它已被 "converted"/绑定到const
左值引用?
类似地,如果 f
调用了一个按值获取 T
实例的函数,
void h(T obj);
和T
有移动构造函数,(即T(T &&);
),是调用移动构造函数,还是调用复制构造函数?
总而言之,如果我们想确保在对右值调用 f
时,右值引用在传递时保持其右值 "status",我们是否必须提供右值引用f
?
值类别应用于表达式,而不是对象。 f
中的 obj
是左值表达式,因此将被视为左值表达式。请注意 g
中的 obj
是 也是 左值表达式;如果表达式是一个对象的名称,那么它就是一个左值。
您所说的能力正是存在转发引用的原因:因此可以通过函数调用保留参数表达式值类别的 copy/move 方面。 f
必须成为 template<typename T> void f(T&& t);
形式的模板,并且在将其传递给 g
.
std::forward
当您将引用的名称用作表达式时,该表达式始终是左值。不管它是左值引用还是右值引用。引用是用左值还是右值初始化也无关紧要。
int &&x = 1;
f(x); // Here `x` is lvalue.
因此在 void f(const T &obj) {...}
中,obj
始终是一个左值,无论您作为参数传递什么。
另请注意,值类别是在编译时确定的。由于 f
不是模板,其中每个表达式的值类别不能取决于您传递的参数。
因此:
If we pass an rvalue to
f
, will the rvalue reference overload ofg
be called
没有
if
f
called a function that takes an instance ofT
by value,void h(T obj);
andT
has a move constructor, (i.e.T(T &&);
), will the move constructor be called
没有
In conclusion, if we wanted to ensure that, when calling
f
on an rvalue, the rvalue reference is passed around keeping its rvalue "status", would we necessarily have to provide an rvalue reference overload forf
?
提供重载是一种选择。请注意,在这种情况下,您必须在右值重载中显式调用 std::move
。
另一种选择是使用转发引用,正如 Nicol Bolas 所建议的:
template <typename T> void f(T &&t)
{
g(std::forward<T>(t));
}
在这里,std::forward
本质上充当了 'conditional move
'。如果传递了右值,它会移动 t
,否则什么都不做。