我知道,除非另外指定,否则所有接受rvalue引用参数的标准库函数都保证将move-from参数保留为有效但未指定状态,并且此处的某些示例可能会显示未定义的行为,但是根本的问题不依赖于此。
以下程序:
// testmove1.cpp
#include <iostream>
#include <string>
int main() {
std::string s1{"String"};
std::cout << "s1: [" << s1 << "]" << std::endl;
std::string s2{std::move(s1)};
std::cout << "s2: [" << s2 << "]" << std::endl;
std::cout << "s1 after move: [" << s1 << "]" << std::endl; // Undefined
return 0;
}
输出:
paul@local:~$ ./testmove1
s1: [String]
s2: [String]
s1 after move: []
paul@local:~$
s1
移动后的输出对我来说似乎是不确定的,但是将字符串留空至少是一个可行的选择。Valgrind报告为该程序进行了一次分配,这是您期望的。
如果我做的事情非常相似,但是有班级成员,我会得到不同的结果:
// testmove2.cpp
#include <iostream>
#include <string>
class MyClass {
std::string m_data;
public:
MyClass(const std::string& data) :
m_data(data) {}
MyClass(const MyClass&& other) :
m_data{std::move(other.m_data)} {};
const std::string& get_data() const { return m_data; }
};
int main() {
MyClass c1{"Object"};
std::cout << "c1: [" << c1.get_data() << "]" << std::endl;
MyClass c2{std::move(c1)};
std::cout << "c2: [" << c2.get_data() << "]" << std::endl;
std::cout << "c1 after move: [" << c1.get_data() << "]" << std::endl;
return 0;
}
输出:
paul@local:~$ ./testmove2
c1: [Object]
c2: [Object]
c1 after move: [Object]
paul@local:~$
不清除第二个字符串似乎也是可行的选择,因此这本身并不令人惊讶。令我惊讶的是,这种行为完全是由于将字符串放入类中而导致的。Valgrind还报告在此进行单个分配。
为了测试他们是否真正指向同一事物,我可以c2
在移动之后进行更改,并检查是否c1
也被更改了:
// testmove3.cpp
#include <iostream>
#include <string>
class MyClass {
std::string m_data;
public:
MyClass(const std::string& data) :
m_data(data) {}
MyClass(const MyClass&& other) :
m_data{std::move(other.m_data)} {};
const std::string& get_data() const { return m_data; }
void change_data() { m_data[0] = 'A'; }
};
int main() {
MyClass c1{"Object"};
std::cout << "c1: [" << c1.get_data() << "]" << std::endl;
MyClass c2{std::move(c1)};
std::cout << "c2: [" << c2.get_data() << "]" << std::endl;
std::cout << "c1 after move: [" << c1.get_data() << "]" << std::endl;
c2.change_data();
std::cout << "c1 after change: [" << c1.get_data() << "]" << std::endl;
std::cout << "c2 after change: [" << c2.get_data() << "]" << std::endl;
return 0;
}
输出:
paul@local:~$ ./testmove3
c1: [Object]
c2: [Object]
c1 after move: [Object]
c1 after change: [Object]
c2 after change: [Abject]
paul@local:~$
在这里,这两个对象显然不是指向同一事物,因为更改c2
不会影响存储在中的对象c1
。Valgrind现在报告2个分配,这显然很必要,因为我们显然有两个不同的字符串来解释观察到的行为,但是对我来说,为什么纯粹由于更改其中之一而突然得到2个分配对我来说并不明显。如果我完全摆脱了这一举动,只是创建c1
然后调用change_data()
它,我只会得到您期望的1分配。
通过删除c1
移动后的所有访问权限,我们可以消除未定义的行为(排除我的其他错误):
// testmove4.cpp
#include <iostream>
#include <string>
class MyClass {
std::string m_data;
public:
MyClass(const std::string& data) :
m_data(data) {}
MyClass(const MyClass&& other) :
m_data{std::move(other.m_data)} {};
const std::string& get_data() const { return m_data; }
void change_data() { m_data[0] = 'A'; }
};
int main() {
MyClass c1{"Object"};
std::cout << "c1: [" << c1.get_data() << "]" << std::endl;
MyClass c2{std::move(c1)};
std::cout << "c2: [" << c2.get_data() << "]" << std::endl;
c2.change_data();
std::cout << "c2 after change: [" << c2.get_data() << "]" << std::endl;
return 0;
}
输出:
paul@local:~$ ./testmove4
c1: [Object]
c2: [Object]
c2 after change: [Abject]
paul@local:~$
显然,我们不再看到c1
不变的事实,因为我们没有输出它。但是Valgrind仍然显示2个分配。
有人知道这是怎么回事吗?
为什么std::string
一个动作本身似乎被零移动,而当它是类成员时却不被零移动?
在最后一个示例中,当我移动一个对象然后更改它时,为什么我得到两个分配而不是一个分配,而当我移动对象却不更改它时却只得到一个分配,为什么?我知道我们似乎正在走向量子计算,但是在C ++中发挥不确定性原理似乎还为时过早。
我正在使用g ++ 4.7.2,但在clang-503.0.40上却得到了相同的观察到的行为。
编辑:仔细考虑一下,如果最终两个对象处于有效状态,则对它们两个都进行分配确实有意义。当可以确定永远不会使用其中的一种分配时,这仅仅是编译器优化了这些分配之一吗?如果有的话,构造一个最小的示例会带来恼人的危险。
我认为这是由于:
MyClass(const MyClass&& other) :
^^^^^
由于对象绑定到other
无法通过该引用进行更改,因此预期的移动操作的效果仅为复制。如果我删除了它,const
那么行为会变回您所期望的:
$ g++ -o tm3 tm3.cc -std=c++11 && ./tm3
c1: [Object]
c2: [Object]
c1 after move: []
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句