微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

转发引用不是被推导为 r 值引用吗?

如何解决转发引用不是被推导为 r 值引用吗?

我有一个关于转发引用的具体问题。 (我认为)我理解 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);
}

Tint,没关系。如果 xint&& 类型,为什么它打印“lvalue”而我们需要 std::forward ?我的意思是,这里从 int&&const int& 的转换在哪里?

解决方法

Types 和 value categories 是表达式的两个独立属性。

每个 C++ expression(带有操作数、文字、变量名等的运算符)都具有两个独立的属性:类型值类别。每个表达式都有一些非引用类型,每个表达式都属于三个主要值类别之一:prvaluexvaluelvalue .

import numpy as np from scipy import interpolate mymin,mymax = 0,3 X = np.linspace(mymin,mymax,4) Y = np.linspace(mymin,4) x,y = np.meshgrid(X,Y) test = np.array([[ 1.2514318,1.25148472,1.25151133],[ 1.25087456,1.25092764,1.25095435],[ 1.25031581,1.25034238,1.25036907,0],[ 0,1.24978222,1.24983587]]) f = interpolate.interp2d(x,y,test,kind='linear') X_n = np.linspace(mymin,4) Y_n = np.linspace(mymin,4) test_n = f(X_n,Y_n) print (test_n) [[ 1.25143180e+00 2.77555756e-16 1.25148472e+00 1.25151133e+00] [ 1.25087456e+00 2.49800181e-16 1.25092764e+00 1.25095435e+00] [ 1.25031581e+00 1.25034238e+00 1.25036907e+00 1.38777878e-17] [ 5.33635770e-17 1.24978222e+00 -1.11022302e-16 1.24983587e+00]] 的类型是 x,但 int&& 是变量名,x 是左值表达式本身,不能绑定到 { {1}}(但可以绑定到 x)。

(强调我的)

以下表达式是左值表达式

  • 变量名、函数int&&或数据成员,无论类型如何,例如const int&,a template parameter object (since C++20)即使变量的类型是右值引用,由其名称组成的表达式也是左值表达式;

这意味着当函数同时具有左值引用和 rvalue-reference 重载时,overload resolution 中也会考虑值类别。

更重要的是,当一个函数同时具有右值引用和左值引用overloads时,右值引用重载绑定到右值(包括右值和xvalues),而左值引用重载绑定到左值:

如果任何参数具有引用类型,则在这一步考虑引用绑定:如果右值参数对应非常量左值引用参数或左值参数对应右值引用参数,则该函数不可行。

std::cin 用于转换为右值或左值,与转发引用参数的原始值类别一致。当左值 std::endl 传递给 std::forward 时,int 被推导出为 foo,那么 T 将是一个左值表达式;当右值 int& 传递给 std::forward<T>(x) 时,int 被推导出为 foo,那么 T 将是一个右值表达式。因此 int 可用于保留原始转发引用参数的值类别。相比之下,std::forward<T>(x) 总是将参数转换为右值表达式。

,

调用 consume(x) 将始终选择 constconsume() 左值引用重载,因为表达式 x 是一个 左值。这与 x 的推导类型无关。

但是,通过调用 consume() 改为:

consume(std::forward<T>(x));

如果传递给foo()的参数的值类别(即带有转发引用的包装函数模板)是一个右值,它将选择右值引用重载 em>.


粗略地说,std::forwardfoo()的参数的原始值类别传播或保留到嵌套调用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 &&) ) 被选中。如果 foo() 被传递一个左值,例如:

foo(i); // selects consume(const int&)

由于 i 是左值,而 std::forward 保留了该参数的原始值类别以调用 consume(),因此选择了左值引用重载——即 {{1} }.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。