std::move 的实现使用到了万能引用?

2022-01-26 21:42:10 +08:00
 amiwrong123

https://www.cnblogs.com/shadow-lr/p/14748272.html

// FUNCTION TEMPLATE move
template <class _Ty>
_NODISCARD constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept {
    return static_cast<remove_reference_t<_Ty>&&>(_Arg);
}

3065 次点击
所在节点    C++
13 条回复
zzxxisme
2022-01-26 23:03:39 +08:00
尝试回答一下:
1. 利用_Ty&&可以接受所有引用类型。如果是左值引用,例如 int&,那么这里"_Ty"就是"int&",整个"_Ty&&"就是"int& &&"。如果是右值引用,例如 int&&,那么_Ty 就是 int 。如果传的是一个值,例如 int ,这样_Ty 也是 int 。
2. std::move 加一个 constexpr 并不意味着返回值一定要加 const ,这个是对函数加的 constexpr ,它意味着这个 std::move 可以在用在一个 constant expression 里面,这样,编译器可以在编译期间进行这个 move 。这个 constexpr 是 c++17 之后加进来的,std::move 是 c++11 就加进来的,我猜可能你看过一些版本是 c++17 之前的?
amiwrong123
2022-01-27 00:35:28 +08:00
@zzxxisme #1
所以,std::move 只有这一个实现也能正常工作,因为它什么类型都可以接,是吧。

附言里我加的这个问题,为什么有了万能引用版本的函数,还能调用到左值引用版本的函数呀?有点不理解了,老哥

>std::move 加一个 constexpr 并不意味着返回值一定要加 const
constexpr int fun() {
return 1;
}

int main()
{
int a = fun();
}
上面这个程序是不是就是你说的意思? constexpr 函数可以赋值给 constexpr 标识符,也可以赋值非常量的标识符(如上程序)。
zzxxisme
2022-01-27 02:07:18 +08:00
@amiwrong123
> 所以,std::move 只有这一个实现也能正常工作,因为它什么类型都可以接,是吧。附言里我加的这个问题,为什么有了万能引用版本的函数,还能调用到左值引用版本的函数呀?有点不理解了,老哥

这里有两点吧。一个是 _Ty&& 的那个的确能接住所有类型。另一个是,当你还写了一个 _Ty& 版本的函数的情况下,因为 main 函数里面的 fun_test(a)传进去的是 a 的引用,也就是传进去的是 A&,这个情况下,编译器会认为 _Ty& 版本的 fun_test 会比 _Ty&& 版本的 fun_test 有更高的 rank (优先级)去匹配这个 A&。所以正确的说法我觉得是,_Ty&&能够接住所有类型,但是在有其他重载的情况下,_Ty&&不一定有更高的 rank 被用上。你可以看看 https://en.cppreference.com/w/cpp/language/overload_resolution 的 Ranking of implicit conversion sequences 下列举的不同情况。

> 上面这个程序是不是就是你说的意思? constexpr 函数可以赋值给 constexpr 标识符,也可以赋值非常量的标识符(如上程序)
对的。你也可以看看这里最下面给的一些例子: https://en.cppreference.com/w/cpp/language/constexpr
zzxxisme
2022-01-27 02:11:37 +08:00
再插一句,如果你把第二个 fun_test 改成
```
template <class _Ty>
void fun_test(_Ty _Arg) { // 这里把&去掉
cout << "left" << endl;
}
```
那么 fun_test(_Ty&&)和 fun_test(_Ty)在匹配 fun_test(a)的时候,编译器会选不出来而报错,因为这两个函数面对 A&的 rank 一样。
amiwrong123
2022-01-27 10:23:24 +08:00
@zzxxisme
在 vs2019 里看了一下,确实 std::move 就只有一个实现
jackchenly
2022-01-27 15:45:33 +08:00
尝试回答
constexpr 好像是告诉编译器,要在编译期推导。
回答完毕
littlewing
2022-01-27 16:31:14 +08:00
_Ty& _Arg 优先级比 _Ty&& _Arg 高
amiwrong123
2022-01-27 23:25:44 +08:00
@zzxxisme #1
@jackchenly #6
@littlewing #7
```cpp
#include <iostream>
using namespace std;

class A {};

A returnTemp() { return A(); }

void test(const A& x) { cout << "left" << endl; }

void test(A&& x) { cout << "right" << endl; }

int main()
{
auto&& c = returnTemp();//万能引用,推断为 A&&
test(std::move(c));

return 0;
}

```
auto&& c 这里也是万能引用,按照万能引用的说法,returnTemp 函数返回一个 A 类型,A 类型经过万能引用推导,应该也是 A 类型呀?
但是我经过了 vs2019 debug 后,发现 c 的类型为 A&&,这是为什么呀?
zzxxisme
2022-01-28 03:03:42 +08:00
> 但是我经过了 vs2019 debug 后,发现 c 的类型为 A&&,这是为什么呀?

这个不太清楚怎么回答。我觉得因为你是用 auto&&去接 returnTemp()产生的临时变量,所以 c 的类型就是 A&&。如果你使用 auto 去接,那 c 的类型就是 A 。但我觉得不管 c 的类型是 A&&还是 A ,它都是维持着 A 类对象的一份 local copy ,用起来都是一样。

> _Ty& _Arg 优先级比 _Ty&& _Arg 高

这个我觉得是有条件的。如果参数本身是 A&这样的引用类型,那_Ty& _Arg 有更高的优先级。如果参数是 A 或者 A&&这样的,_Ty&&有更高的优先级。
littlewing
2022-01-28 17:46:48 +08:00
@amiwrong123
auto&& c = returnTemp();//万能引用,推断为 A&&
的原因是函数返回值都是右值
littlewing
2022-01-28 17:48:46 +08:00
@zzxxisme
> _Ty& _Arg 优先级比 _Ty&& _Arg 高
这个说法是针对你的例子,因为你传进去的参数是左值引用,所以 Ty& _Arg 比 _Ty&& _Arg 优先级更高
littlewing
2022-01-28 17:52:05 +08:00
littlewing
2022-01-28 17:56:49 +08:00
> _Ty& _Arg 优先级比 _Ty&& _Arg 高
应该改成,在你传入 左值引用的时候,_Ty& _Arg 的 匹配度比 _Ty&& _Arg 更高,所以选择了前者

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/830799

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX