Cro*_*man 1 c++ move-semantics
我知道的是,除非另有说明,所有的标准库函数接受右值引用参数都保证要离开移动,从参数中有效,但不确定状态,而这里的一些例子可以表现出不确定的行为结果,而是根本问题不依赖于此.
以下程序:
// 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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
paul@local:~$ ./testmove1
s1: [String]
s2: [String]
s1 after move: []
paul@local:~$
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
输出:
paul@local:~$ ./testmove2
c1: [Object]
c2: [Object]
c1 after move: [Object]
paul@local:~$
Run Code Online (Sandbox Code Playgroud)
没有清除第二个字符串也似乎是一个可行的替代方案,所以这本身并不令人惊讶.令我惊讶的是,这种行为完全不同于将字符串放入类中的结果.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;
}
Run Code Online (Sandbox Code Playgroud)
哪个输出:
paul@local:~$ ./testmove3
c1: [Object]
c2: [Object]
c1 after move: [Object]
c1 after change: [Object]
c2 after change: [Abject]
paul@local:~$
Run Code Online (Sandbox Code Playgroud)
在这里,两个对象显然没有指向同一个东西,因为改变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;
}
Run Code Online (Sandbox Code Playgroud)
哪个输出:
paul@local:~$ ./testmove4
c1: [Object]
c2: [Object]
c2 after change: [Abject]
paul@local:~$
Run Code Online (Sandbox Code Playgroud)
我们显然不再看到c1
不改变的事实,因为我们没有输出它.但Valgrind仍显示2次分配.
谁知道发生了什么,这里?
为什么a std::string
在移动之后似乎被归零,而不是当它是一个类成员时?
在最后一个例子中,当我移动一个对象然后更改它时,为什么我得到两个分配而不是一个,当我移动对象时只得到一个分配然后不改变它?我知道我们似乎正朝着量子计算方向发展,但是在C++中使用不确定性原则似乎还为时过早.
我正在使用g ++ 4.7.2,但我在clang-503.0.40上得到了相同的观察行为.
编辑:考虑一下,如果最终有两个对象处于有效状态,那么它们都有分配的意义.这只是编译器在确定其中一个分配永远不会被使用时优化其中一个分配的吗?构建最小例子的烦人危险,如果是的话.
我认为这是由于:
MyClass(const MyClass&& other) :
^^^^^
Run Code Online (Sandbox Code Playgroud)
由于绑定的对象 other
不能通过此引用进行更改,因此预期的移动操作的效果仅仅是副本.如果我删除了这个,const
那么行为会改变回你的期望:
$ g++ -o tm3 tm3.cc -std=c++11 && ./tm3
c1: [Object]
c2: [Object]
c1 after move: []
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1581 次 |
最近记录: |