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);
}
T&
去接一个T
,这是另一个问题)如果可以的话,那万能引用就是任何类型都能接得。const T&&
,这是一个底层 const ,具有底层 const 的引用在传参过程是不能丢失掉底层 const 的,所以 这不是减少了 std::move 返回值的使用范围了吗?#include <iostream>
using namespace std;
class A {};
template <class _Ty>
void fun_test(_Ty&& _Arg) {
cout << "wanneng" << endl;
}
template <class _Ty>
void fun_test(_Ty& _Arg) {
cout << "left" << endl;
}
int main()
{
A a;
fun_test(a);
}
现在我写了两个函数,第一个是万能引用版本,第二个是左值引用版本。 我理解,因为有了万能引用版本,万能引用什么类型都可以接,所以不可能调用到 第二个函数了.
但是,打印的是 left 这是为什么?
1
zzxxisme 2022-01-26 23:03:39 +08:00 2
尝试回答一下:
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 之前的? |
2
amiwrong123 OP @zzxxisme #1
所以,std::move 只有这一个实现也能正常工作,因为它什么类型都可以接,是吧。 附言里我加的这个问题,为什么有了万能引用版本的函数,还能调用到左值引用版本的函数呀?有点不理解了,老哥 >std::move 加一个 constexpr 并不意味着返回值一定要加 const constexpr int fun() { return 1; } int main() { int a = fun(); } 上面这个程序是不是就是你说的意思? constexpr 函数可以赋值给 constexpr 标识符,也可以赋值非常量的标识符(如上程序)。 |
3
zzxxisme 2022-01-27 02:07:18 +08:00 1
@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 |
4
zzxxisme 2022-01-27 02:11:37 +08:00 1
再插一句,如果你把第二个 fun_test 改成
``` template <class _Ty> void fun_test(_Ty _Arg) { // 这里把&去掉 cout << "left" << endl; } ``` 那么 fun_test(_Ty&&)和 fun_test(_Ty)在匹配 fun_test(a)的时候,编译器会选不出来而报错,因为这两个函数面对 A&的 rank 一样。 |
5
amiwrong123 OP @zzxxisme
在 vs2019 里看了一下,确实 std::move 就只有一个实现 |
6
jackchenly 2022-01-27 15:45:33 +08:00
尝试回答
constexpr 好像是告诉编译器,要在编译期推导。 回答完毕 |
7
littlewing 2022-01-27 16:31:14 +08:00
_Ty& _Arg 优先级比 _Ty&& _Arg 高
|
8
amiwrong123 OP @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&&,这是为什么呀? |
9
zzxxisme 2022-01-28 03:03:42 +08:00 1
> 但是我经过了 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&&有更高的优先级。 |
10
littlewing 2022-01-28 17:46:48 +08:00
|
11
littlewing 2022-01-28 17:48:46 +08:00
|
12
littlewing 2022-01-28 17:52:05 +08:00
|
13
littlewing 2022-01-28 17:56:49 +08:00
> _Ty& _Arg 优先级比 _Ty&& _Arg 高
应该改成,在你传入 左值引用的时候,_Ty& _Arg 的 匹配度比 _Ty&& _Arg 更高,所以选择了前者 |