代码之家  ›  专栏  ›  技术社区  ›  ar2015

std::move如何使原始变量的值无效?

  •  -2
  • ar2015  · 技术社区  · 6 年前

    在下面的示例中 cpp reference :

    #include <iostream>
    #include <utility>
    #include <vector>
    #include <string>
    
    int main()
    {
        std::string str = "Hello";
        std::vector<std::string> v;
    
        // uses the push_back(const T&) overload, which means 
        // we'll incur the cost of copying str
        v.push_back(str);
        std::cout << "After copy, str is \"" << str << "\"\n";
    
        // uses the rvalue reference push_back(T&&) overload, 
        // which means no strings will be copied; instead, the
        // Contents of str will be moved into the vector.  This is
        // less expensive, but also means str might now be empty.
        v.push_back(std::move(str));
        std::cout << "After move, str is \"" << str << "\"\n";
    
        std::cout << "The contents of the vector are \"" << v[0]
                  << "\", \"" << v[1] << "\"\n";
    }
    

    std::move 可能导致原值丢失。在我看来

    v.push_back(std::move(str))
    

    v[1] 被创造。那么,

    &v[1] = &str
    

    但为什么它会损害 str ? 这没有道理。

    有很多复杂的教程 这比我自己的问题更难理解。

    有人能写信吗

    v、 向后推(std::move(str))
    

    等同于 c++03 ?

    我寻找一个解释,其理解是容易的,不包含诸如 x-value , static_cast remove_reference 等等,因为他们自己需要理解 第一。请避免这种循环依赖关系。

    7510182 , 3413470

    因为我想知道 str公司 是伤害而不是伤害 五[1] .

    c++03语言


    更新:为了避免复杂化,让我们考虑一个更简单的例子 int 如下所示

    int x = 10;
    int y = std::move(x);
    std::cout << x;
    
    2 回复  |  直到 6 年前
        1
  •  4
  •   t.niese    6 年前

    根据实现的不同 std::move 可能是内部内存地址的简单交换。

    http://cpp.sh/9f6ru

    #include <iostream>
    #include <string>
    
    int main()
    {
      std::string str1 = "test";
      std::string str2 = "test2";
    
      std::cout << "str1.data() before move: "<< static_cast<const void*>(str1.data()) << std::endl;
      std::cout << "str2.data() before move: "<< static_cast<const void*>(str2.data()) << std::endl;
    
      str2 = std::move(str1);
      std::cout << "=================================" << std::endl;
    
      std::cout << "str1.data() after move: " << static_cast<const void*>(str1.data()) << std::endl;
      std::cout << "str2.data() after move: " << static_cast<const void*>(str2.data()) << std::endl;
    }
    

    您将得到以下输出:

    str1.data() before move: 0x363d0d8
    str2.data() before move: 0x363d108
    =================================
    str1.data() after move: 0x363d108
    str2.data() after move: 0x363d0d8
    

    但是实现细节可能更复杂 http://cpp.sh/6dx7j . 如果您查看您的示例,您将看到为字符串创建副本并不一定需要为其内容分配新内存。这是因为几乎所有的操作 std::string 是只读的或需要分配内存。因此,实现可以决定只做浅拷贝:

    #include <iostream>
    #include <string>
    #include <vector>
    
    int main()
    {
      std::string str = "Hello";
      std::vector<std::string> v;
    
      std::cout << "str.data() before move: "<< static_cast<const void*>(str.data()) << std::endl;
    
      v.push_back(str);
      std::cout << "============================" << std::endl;
      std::cout << "str.data()  after push_back: "<< static_cast<const void*>(str.data()) << std::endl;
      std::cout << "v[0].data() after push_back: "<< static_cast<const void*>(v[0].data()) << std::endl;
    
      v.push_back(std::move(str));
      std::cout << "============================" << std::endl;
    
      std::cout << "str.data()  after move: "<< static_cast<const void*>(str.data()) << std::endl;
      std::cout << "v[0].data() after move: "<< static_cast<const void*>(v[0].data()) << std::endl;
      std::cout << "v[1].data() after move: "<< static_cast<const void*>(v[1].data()) << std::endl;
      std::cout << "After move, str is \"" << str << "\"\n";
    
    
      str = std::move(v[1]);
      std::cout << "============================" << std::endl;
      std::cout << "str.data()  after move: "<< static_cast<const void*>(str.data()) << std::endl;
      std::cout << "v[0].data() after move: "<< static_cast<const void*>(v[0].data()) << std::endl;
      std::cout << "v[1].data() after move: "<< static_cast<const void*>(v[1].data()) << std::endl;
      std::cout << "After move, str is \"" << str << "\"\n";
    }
    

    str.data() before move: 0x3ec3048
    ============================
    str.data()  after push_back: 0x3ec3048
    v[0].data() after push_back: 0x3ec3048
    ============================
    str.data()  after move: 0x601df8
    v[0].data() after move: 0x3ec3048
    v[1].data() after move: 0x3ec3048
    After move, str is ""
    ============================
    str.data()  after move: 0x3ec3048
    v[0].data() after move: 0x3ec3048
    v[1].data() after move: 0x601df8
    After move, str is "Hello"
    

    如果你看看:

    #include <iostream>
    #include <string>
    #include <vector>
    
    int main()
    {
      std::string str = "Hello";
      std::vector<std::string> v;
    
      std::cout << "str.data() before move: "<< static_cast<const void*>(str.data()) << std::endl;
    
      v.push_back(str);
      std::cout << "============================" << std::endl;
      str[0] = 't';
      std::cout << "str.data()  after push_back: "<< static_cast<const void*>(str.data()) << std::endl;
      std::cout << "v[0].data() after push_back: "<< static_cast<const void*>(v[0].data()) << std::endl;
    
    }
    

    那么你会认为 str[0] = 't' 只是把数据放回原处。但事实并非如此 http://cpp.sh/47nsy .

    str.data() before move: 0x40b8258
    ============================
    str.data()  after push_back: 0x40b82a8
    v[0].data() after push_back: 0x40b8258
    

    移动原语,比如:

    void test(int i) {
      int x=i;
      int y=std::move(x);
      std::cout<<x;
      std::cout<<y;
    }
    

    主要是由编译器完全优化出来的:

      mov ebx, edi
      mov edi, offset std::cout
      mov esi, ebx
      call std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
      mov edi, offset std::cout
      mov esi, ebx
      pop rbx
      jmp std::basic_ostream<char, std::char_traits<char> >::operator<<(int) # TAILCALL
    

    std::cout 使用相同的寄存器 x y

        2
  •  4
  •   Deduplicator    6 年前

    std::move 是对rvalue引用的简单强制转换。实际上不是

    所有的魔法都发生在函数中 接收 掠夺许可证

    因此,使用移动语义通常效率更高(一个人可以做得更快,相信我),并且不太可能抛出异常(资源获取容易失败),因为这样做的代价是破坏源代码。

    所有这些都是由一个叫做 标准::移动 ,但它本身