V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
YyYyYyy
V2EX  ›  C

请教一个 G++在 C++98 下为什么把 move semantics 模仿的这么熟练的问题

  •  
  •   YyYyYyy · 2017-11-28 17:47:31 +08:00 · 2658 次点击
    这是一个创建于 2312 天前的主题,其中的信息可能已经有所发展或是发生改变。



    为什么会变成这样呢......明明用的不是 C++11。明明已经关了 copy elision。两件快乐事情重合在一起。而这两份快乐,又给我带来了更多的快乐。得到的,本该是两个不同的地址......但是,为什么,会变成这样呢......

    gcc 版本 6.4.0
    第 1 条附言  ·  2017-11-28 23:02:12 +08:00
    给某些说我没关掉 NRVO 的人的参考:



    ------

    感谢各位分享思路,自己写了类 copy ctor 有正常调两遍。
    只是这里的 STL 嘛……看来我得学会读汇编才行(又一个大坑,感觉要填不完了……)
    24 条回复    2017-11-29 05:49:19 +08:00
    noli
        1
    noli  
       2017-11-28 17:55:38 +08:00
    就算得到了相同的地址,也不代表就没有 copy 吧?
    内部可能使用了 realloc 并且成功了 。
    rogerchen
        2
    rogerchen  
       2017-11-28 17:56:25 +08:00 via iPhone
    rvo 关不掉的
    noli
        3
    noli  
       2017-11-28 18:00:40 +08:00
    @rogerchen #2 我不认为楼主的例子里满足 RVO 的条件。
    YyYyYyy
        4
    YyYyYyy  
    OP
       2017-11-28 18:42:29 +08:00
    @noli 在发这个帖子之前这问题在某 tg 群里讨论过,得到的结论也是“炸内存后原地 alloc ”。
    只是无法对这种结论证伪,所以来这里问问有没有其他经验人士能否得到不同看法。
    wevsty
        5
    wevsty  
       2017-11-28 19:16:48 +08:00
    看看生成的汇编代码就知道编译器是怎么处理的了。
    QAPTEAWH
        6
    QAPTEAWH  
       2017-11-28 19:18:33 +08:00 via iPhone
    @noli 我觉得显然满足 RVO..
    mooncakejs
        7
    mooncakejs  
       2017-11-28 19:38:52 +08:00
    @noli 我也觉得满足 rvo,因为退出函数后, 就没有代码能碰到里面的 v 对象。
    换成参数传进去呢?
    koebehshian
        8
    koebehshian  
       2017-11-28 20:18:50 +08:00   ❤️ 1
    碰巧一样呗。函数 fun 执行完毕后,变量 v 就析构了,在堆上申请的内存也释放了,0x600000510 这个地址指向的内存没人用,恰好被 main 中的 v 内部申请内存时申请到。copy elision 应该指 v 这个对象,所以 0xffffcb50,0xffffcbb0 两个地址不同,而它们各自申请的堆内存可以恰好相同
    noli
        9
    noli  
       2017-11-28 20:39:58 +08:00
    @QAPTEAWH @mooncakejs

    请问两位怎么区分 Return Value Optimization 和 Named Return Value Optimization ?

    我不认为 return v 中的 v 是纯右值,所以这里不会发生 RVO。
    mooncakejs
        10
    mooncakejs  
       2017-11-28 21:05:31 +08:00
    @noli 我单纯的认为这是个编译器优化,因为退出 fun 后,fun 中的 v 变量已经没有任何代码可以访问,可能即使没有开-O 选项,它也可能做了这个优化。 想要测试下是不是偶然的话,在 fun 函数里,多创建几次变量,调整变量顺序再试试。
    jmc891205
        11
    jmc891205  
       2017-11-28 21:44:10 +08:00   ❤️ 1
    打死白学家
    noli
        12
    noli  
       2017-11-28 21:48:52 +08:00
    @mooncakejs #10

    编译器开优化也要讲基本法的。

    http://en.cppreference.com/w/cpp/language/copy_elision

    When a **NAMELESS** temporary, not bound to any references, would be copied or moved (since C++11) into an object of the same type (ignoring top-level cv-qualification), the copy/move (since C++11) is omitted. When that temporary is constructed, ** it is constructed directly in the storage where it would** otherwise be copied or moved (since C++11) to. When the nameless temporary is the argument of a return statement, this variant of copy elision is known as RVO, "return value optimization".

    NAMELESS 的意思懂吧?就是不能绑定到任何一个变量名(否则就变成了左值了)

    如果像你说的,没有东西能碰 v 就能 RVO,那编译器怎么知道 v 里面的不会被其他对象访问?
    只要有变成左值的机会,就无法消除这个可能。
    LPeJuN6lLsS9
        13
    LPeJuN6lLsS9  
       2017-11-28 22:05:01 +08:00
    @noli 但是这里它满足了 named rvo 的条件啊?编译器上帝视角,它当然知道这个变量出了函数作用于就要死了,它又不是参数等等
    详细解释就在你摘抄的那段的上面
    LPeJuN6lLsS9
        14
    LPeJuN6lLsS9  
       2017-11-28 22:10:10 +08:00
    @YyYyYyy 你自己写个类,然后做好移动方法,看看编译器是不是真的用了你的方法就行
    noli
        15
    noli  
       2017-11-28 22:14:42 +08:00
    @hantsuki #13

    同样也不满足 NRVO,因为 v 已经作为某个函数的参数适用过了。

    v.push_back
    v.data

    经过这些调用(作为函数参数出现)之后还能 NRVO 的话,请问什么不能 NRVO ?
    waruqi
        16
    waruqi  
       2017-11-28 23:19:05 +08:00 via Android
    别纠结了 用 c 吧。
    fenixan2010
        17
    fenixan2010  
       2017-11-28 23:25:35 +08:00
    @noli 按楼主的更新不加-fno-elide-constructors 的话这里确实有用到 NRVO
    LPeJuN6lLsS9
        18
    LPeJuN6lLsS9  
       2017-11-28 23:44:50 +08:00   ❤️ 1
    @noli 你想说 v.push_back 里用了 this 就算“参数”了?问题是“ which isn't a function parameter ”你读得懂吗,parameter 和 argument 不是同个意思
    LPeJuN6lLsS9
        19
    LPeJuN6lLsS9  
       2017-11-28 23:47:40 +08:00
    @hantsuki #12 c++98 好像没有移动方法?犯蠢了,没用过 98
    noli
        20
    noli  
       2017-11-28 23:56:51 +08:00
    @hantsuki #18 @fenixan2010 # 17

    我同意你们的说法,v 确实不是“参数”。
    是否会产生 NRVO,确实有这个可能,我之前想当然了
    ——虽然我觉得编译器这么推测有点危险,但确实符合 NRVO 的条件。

    但是楼主的例子里面,加上那个 -fno-elide-constructors 之后应该就不会有了。
    ccsexyz
        21
    ccsexyz  
       2017-11-29 00:01:12 +08:00
    我认为是这样的,这里 v = func() 有两次拷贝构造函数 vector(const vector &)。
    func:v --> tmp:v 拷贝构造 --> func:v 析构 --> main:v 拷贝构造(分配的存储区域刚好和 func:v 存储区域一致) --> tmp:v 析构
    LPeJuN6lLsS9
        22
    LPeJuN6lLsS9  
       2017-11-29 00:08:14 +08:00 via Android
    @noli 就是想反驳一下你说的不满足 nrvo。楼主那是咋回事我还没去想

    用 msvc2017 开 release 模式做了个简单实验,也是这样调用了一个方法后才返回,确实没有移动和复制构造。

    为啥会觉得危险呢,我觉得 nrvo 大前提是一个自动变量,这就很安全了
    lrxiao
        23
    lrxiao  
       2017-11-29 02:17:25 +08:00
    NRVO 关不掉
    符合 NRVO
    但是 asm 是正常的调用两次 copy ctor fun 中和 main 中 data()最后同一地址 但是临时对象不是
    gnaggnoyil
        24
    gnaggnoyil  
       2017-11-29 05:49:19 +08:00
    C++98 我不熟,不过我想提醒那些说 NRVO 的人,`std::vector<int> v=fun()`还需要一次构造过程呢.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3322 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 13:21 · PVG 21:21 · LAX 06:21 · JFK 09:21
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.