转发引用不是推导为 r 值引用吗?
Aren't forwarding references deduced as r-value references?
我有一个关于转发参考的具体问题。 (我认为)我理解 r 值引用和 std::move
,但我很难理解转发引用:
#include <iostream>
#include <utility>
template <typename T> class TD; // from "Effective Modern C++"
void consume(const int &i) { std::cout << "lvalue\n"; }
void consume(int &&i) { std::cout << "rvalue\n"; }
template <typename T>
void foo(T&& x) {
// TD<decltype(x)> xType; - prints int&&
consume(x);
}
int main() {
foo(1 + 2);
}
T
是 int
,没关系。如果 x
是 int&&
类型,为什么它打印“左值”而我们需要 std::forward
?我的意思是,从 int&&
到 const int&
的转换在哪里?
调用 consume(x)
总是 select consume()
的 const
左值引用重载 因为表达式 x
是一个 左值 。这与 x
.
的推导类型无关
但是,通过调用 consume()
而不是:
consume(std::forward<T>(x));
它将select右值引用重载如果参数的值类别传递给foo()
(即具有转发引用的包装函数模板) 是一个 rvalue.
粗略地说,std::forward
将foo()
的参数的原始值类别传播或保留到嵌套调用的参数consume()
:
template <typename T>
void foo(T&& x) {
// preserve original value category of foo()'s argument
consume(std::forward<T>(x));
}
也就是说,如果 foo()
传递了一个右值,例如:
foo(1); // selects consume(int &&)
这个参数的值类别 - 1
,一个右值 - 通过 std::forward
进一步传播到对 consume()
的调用,因此右值引用重载(即 void consume(int &&)
) 是 selected。如果 foo()
传递了一个左值,例如:
foo(i); // selects consume(const int&)
因为 i
是一个左值,并且 std::forward
保留了这个参数的原始值类别到对 consume()
的调用,所以左值引用重载是 selected – 即 void consume(const int&)
.
类型和value categories是表达式的两个独立属性。
Each C++ expression (an operator with its operands, a literal, a variable name, etc.) is characterized by two independent properties: a type and a value category. Each expression has some non-reference type, and each expression belongs to exactly one of the three primary value categories: prvalue, xvalue, and lvalue.
x
的类型是int&&
,但是x
是变量名,x
是左值表达式本身,就是不能绑定到 int&&
(但可以绑定到 const int&
)。
(强调我的)
The following expressions are lvalue expressions:
- the name of a variable, a function
, a template parameter object (since C++20)
, or a data member, regardless of type, such as std::cin
or std::endl
. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;
这意味着当函数同时具有左值引用和 rvalue-reference overloads, value categories are considered in overload resolution 时。
More importantly, when a function has both rvalue reference and lvalue reference overloads, the rvalue reference overload binds to rvalues (including both prvalues and xvalues), while the lvalue reference overload binds to lvalues:
If any parameter has reference type, reference binding is accounted for at this step: if an rvalue argument corresponds to non-const lvalue reference parameter or an lvalue argument corresponds to rvalue reference parameter, the function is not viable.
std::forward
用于右值或左值的转换,与转发引用参数的原始值类别一致。当左值int
传递给foo
时,T
被推导为int&
,那么std::forward<T>(x)
将是一个左值表达式;当右值 int
传递给 foo
时,T
被推导为 int
,那么 std::forward<T>(x)
将是一个右值表达式。因此 std::forward
可用于保留原始转发引用参数的值类别。作为对比 std::move
总是将参数转换为右值表达式。
我有一个关于转发参考的具体问题。 (我认为)我理解 r 值引用和 std::move
,但我很难理解转发引用:
#include <iostream>
#include <utility>
template <typename T> class TD; // from "Effective Modern C++"
void consume(const int &i) { std::cout << "lvalue\n"; }
void consume(int &&i) { std::cout << "rvalue\n"; }
template <typename T>
void foo(T&& x) {
// TD<decltype(x)> xType; - prints int&&
consume(x);
}
int main() {
foo(1 + 2);
}
T
是 int
,没关系。如果 x
是 int&&
类型,为什么它打印“左值”而我们需要 std::forward
?我的意思是,从 int&&
到 const int&
的转换在哪里?
调用 consume(x)
总是 select consume()
的 const
左值引用重载 因为表达式 x
是一个 左值 。这与 x
.
但是,通过调用 consume()
而不是:
consume(std::forward<T>(x));
它将select右值引用重载如果参数的值类别传递给foo()
(即具有转发引用的包装函数模板) 是一个 rvalue.
粗略地说,std::forward
将foo()
的参数的原始值类别传播或保留到嵌套调用的参数consume()
:
template <typename T>
void foo(T&& x) {
// preserve original value category of foo()'s argument
consume(std::forward<T>(x));
}
也就是说,如果 foo()
传递了一个右值,例如:
foo(1); // selects consume(int &&)
这个参数的值类别 - 1
,一个右值 - 通过 std::forward
进一步传播到对 consume()
的调用,因此右值引用重载(即 void consume(int &&)
) 是 selected。如果 foo()
传递了一个左值,例如:
foo(i); // selects consume(const int&)
因为 i
是一个左值,并且 std::forward
保留了这个参数的原始值类别到对 consume()
的调用,所以左值引用重载是 selected – 即 void consume(const int&)
.
类型和value categories是表达式的两个独立属性。
Each C++ expression (an operator with its operands, a literal, a variable name, etc.) is characterized by two independent properties: a type and a value category. Each expression has some non-reference type, and each expression belongs to exactly one of the three primary value categories: prvalue, xvalue, and lvalue.
x
的类型是int&&
,但是x
是变量名,x
是左值表达式本身,就是不能绑定到 int&&
(但可以绑定到 const int&
)。
(强调我的)
The following expressions are lvalue expressions:
- the name of a variable, a function
, a template parameter object (since C++20)
, or a data member, regardless of type, such asstd::cin
orstd::endl
. Even if the variable's type is rvalue reference, the expression consisting of its name is an lvalue expression;
这意味着当函数同时具有左值引用和 rvalue-reference overloads, value categories are considered in overload resolution 时。
More importantly, when a function has both rvalue reference and lvalue reference overloads, the rvalue reference overload binds to rvalues (including both prvalues and xvalues), while the lvalue reference overload binds to lvalues:
If any parameter has reference type, reference binding is accounted for at this step: if an rvalue argument corresponds to non-const lvalue reference parameter or an lvalue argument corresponds to rvalue reference parameter, the function is not viable.
std::forward
用于右值或左值的转换,与转发引用参数的原始值类别一致。当左值int
传递给foo
时,T
被推导为int&
,那么std::forward<T>(x)
将是一个左值表达式;当右值 int
传递给 foo
时,T
被推导为 int
,那么 std::forward<T>(x)
将是一个右值表达式。因此 std::forward
可用于保留原始转发引用参数的值类别。作为对比 std::move
总是将参数转换为右值表达式。