std::move 的实现使用到了万能引用? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
amiwrong123
V2EX    C++

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

  •  
  •   amiwrong123 2022-01-26 21:42:10 +08:00 3707 次点击
    这是一个创建于 1409 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    // FUNCTION TEMPLATE move template <class _Ty> _NODISCARDconstexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept { return static_cast<remove_reference_t<_Ty>&&>(_Arg); } 
    • 我看这个形式是完全符合万能引用的,所以 std::move 理论上来说只需要这一个实现就行了,是吗?因为任何引用类型(左值引用、右值引用)都可以被 万能引用接受
      • (而且我理解,一个非引用类型也可以被万能引用接受吧?就相当于T&去接一个T,这是另一个问题)如果可以的话,那万能引用就是任何类型都能接得。
    • 为什么 std::move 要加 constexpr 呀,那返回值类型不就变成了const T&&,这是一个底层 const ,具有底层 const 的引用在传参过程是不能丢失掉底层 const 的,所以 这不是减少了 std::move 返回值的使用范围了吗?
      • 而且我看其他博客里,std::move 是没有带 constexpr 的,所以到底带不带?
    第 1 条附言    2022-01-27 00:30:12 +08:00
    #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 这是为什么?

    13 条回复    2022-01-28 17:56:49 +08:00
    zzxxisme
        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 之前的?
    amiwrong123
        2
    amiwrong123  
    OP
       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
        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
    zzxxisme
        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 一样。
    amiwrong123
        5
    amiwrong123  
    OP
       2022-01-27 10:23:24 +08:00
    @zzxxisme
    在 vs2019 里看了一下,确实 std::move 就只有一个实现
    jackchenly
        6
    jackchenly  
       2022-01-27 15:45:33 +08:00
    尝试回答
    constexpr 好像是告诉编译器,要在编译期推导。
    回答完毕
    littlewing
        7
    littlewing  
       2022-01-27 16:31:14 +08:00
    _Ty& _Arg 优先级比 _Ty&& _Arg 高
    amiwrong123
        8
    amiwrong123  
    OP
       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
        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&&有更高的优先级。
    littlewing
        10
    littlewing  
       2022-01-28 17:46:48 +08:00
    @amiwrong123
    auto&& c = returnTemp();//万能引用,推断为 A&&
    的原因是函数返回值都是右值
    littlewing
        11
    littlewing  
       2022-01-28 17:48:46 +08:00
    @zzxxisme
    > _Ty& _Arg 优先级比 _Ty&& _Arg 高
    这个说法是针对你的例子,因为你传进去的参数是左值引用,所以 Ty& _Arg 比 _Ty&& _Arg 优先级更高
    littlewing
        13
    littlewing  
       2022-01-28 17:56:49 +08:00
    > _Ty& _Arg 优先级比 _Ty&& _Arg 高
    应该改成,在你传入 左值引用的时候,_Ty& _Arg 的 匹配度比 _Ty&& _Arg 更高,所以选择了前者
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     1428 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 16:52 PVG 00:52 LAX 08:52 JFK 11:52
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86