当 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,否则什么都不做。