std :: move如何使原始变量的值无效? [英] How does std::move invalidates the value of original variable?
问题描述
在来自 cpp参考的以下示例中:
#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
可能会导致原始值丢失.在我看来,
Using std::move
may cause the original value be lost. To me, it looks like
v.push_back(std::move(str))
将导致创建新成员v[1]
.然后,
causes a new member v[1]
being created. Then,
&v[1] = &str
但是为什么要破坏str
中的值?这没有道理.
But why should it damage the value in str
? It does not make sense.
关于std::move
的教程很复杂,比我自己的问题难理解.
There are many complicated tutorials about std::move
which are harder than my own question to understand.
任何人都可以写
v.push_back(std::move(str))
等同于使用c++03
吗?
我正在寻找一个易于理解的解释,并且不包含诸如x-value
,static_cast
和remove_reference
等先决条件,因为它们本身首先需要理解std::move
.请避免这种循环依赖.
I look for an explanation whose understanding is easy and do not contain prerequisites such as x-value
, static_cast
, remove_reference
, etc, as they themselves require to understand std::move
first. Please avoid this circular dependency.
这些链接也无法回答我的问题: 7510182 , 3413470
Also these links do not answer my question: 7510182, 3413470
因为我有兴趣了解str
的危害,而不是v[1]
的危害.
Because I am interested in knowing how str
is harm and not what happens to v[1]
.
只要是c++03
一样简单,也欢迎使用伪代码.
Pseudo code is also welcome as far as it is as simple as c++03
.
更新:为避免复杂起见,让我们考虑一个更简单的int
示例,如下所示
Update: To avoid complication, let's consider a simpler example of int
as follows
int x = 10;
int y = std::move(x);
std::cout << x;
推荐答案
根据实现的不同,std::move
可以是内部存储器地址的简单交换.
Depending on the implementation, the std::move
could be a simple swap of the internal memory addresses.
如果您在 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
但是结果可能会有所不同,具体取决于编译器和std库的实现.
But the result may vary depending on the implementation of the compiler and the std library.
但是实现细节可能更加复杂 http://cpp.sh/6dx7j .如果看一下示例,您将看到为字符串创建副本不一定需要为其内容分配新的内存.这是因为std::string
上的几乎所有操作都是只读的,或者需要分配内存.因此,实现可以决定只做浅表副本:
But the implementation details can be even more complex http://cpp.sh/6dx7j. If you look at your example, then you will see that creating a copy for a string does not necessarily require that new memory for its content is allocated. This is because nearly all operations on std::string
are read only or require the allocation of memory. So the implementation can decide to do just shallow copies:
#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 .
Then you would assume that str[0] = 't'
would just replace the data in place. But this is not necessarily the case 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;
}
大部分将由编译器完全优化:
Would be mostly be optimized out completely by the compiler:
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
完全被优化.
Both std::cout
used the same register, the x
and y
are completely optimized away.
这篇关于std :: move如何使原始变量的值无效?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!