我认为以下示例与std::map::emplace()
具有相同的结果,但它没有:
struct MoveTest {
MoveTest():
var(true)
{
cout << "ctor: " << static_cast<void*>(&var) << endl;
}
MoveTest(MoveTest&& mt):
var(std::move(mt.var))
{
cout << "move ctor: " << static_cast<void*>(&var) << endl;
}
bool var;
};
int main() {
map<int, MoveTest> mtest;
mtest.insert(make_pair(1, MoveTest()));
return 0;
}
输出:
ctor: 0x7fff12e4e19b
move ctor: 0x7fff12e4e194
move ctor: 0x25f3034
MoveTest::var
在每次移动中都有不同的地址。它看起来不像是“ move ”。我的代码或理解有什么问题?
答案 0 :(得分:5)
有关MoveTest为你定义它,有没有真正太多的举动构造函数(或赋值运算符)可以优化(或通常做的 - 一切能做的,只能是从源头去目的地复制)
当一个对象包含指向堆上分配的一堆外部存储器的指针时,移动主要是一个很大的胜利。在这种情况下,移动ctor /赋值运算符基本上可以执行浅拷贝(即,只是从移动的对象中抓取指针)而不是深拷贝(复制该指针引用的所有数据)。
例如:
class move_tst {
int *buffer;
static const int size = 1024 * 1024;
public:
move_tst() {
buffer = new int[size];
std::iota(buffer, buffer + size, 0);
}
move_tst(move_tst const &other) {
buffer = new int[size];
std::copy_n(other.buffer, size, buffer);
}
#ifdef MOVE
move_tst(move_tst &&other) {
buffer = other.buffer;
other.buffer = nullptr;
}
#endif
~move_tst() {
delete [] buffer;
}
};
警告:我在这里使用了new
的原始调用和原始指针,因此没有其他任何内容涉及,我们不会(例如)通过智能指针获得移动语义。对于正常情况下的普通代码,你应该不使用(原始指针或new
的原始调用,我的意思)。
编辑:就std::move
所做的而言,它实际上并没有进行移动 - 它只是表示您不再关心某个值,因此它有资格成为移动的来源,即使这会摧毁它的价值。
例如,对于上面的课程,您可以进行如下测试:
for (int i = 0; i < 1000; i++) {
move_tst src;
move_tst dst =src;
}
...并将其与:
进行比较for (int i = 0; i < 1000; i++) {
move_tst src;
move_tst dst = std::move(src);
}
如果没有std::move
,dst
将被创建为src
的副本,但使用std::move
,它将能够从{{src
移动1}}到dst
。
答案 1 :(得分:1)
您的日志只是告诉您使用默认构造函数
创建临时MoveTest实例MoveTest()
然后将复制到其他位置的对象
中make_pair(1, MoveTest())
并再次复制到地图对象内的某处
mtest.insert(make_pair(1, MoveTest()));
三个不同的MoveTest实例有三个构造函数调用,每个实例都有自己的地址。正如已经很好地解释的那样,构造函数是移动构造函数还是旧式复制构造函数并不重要。
您是否期望将某些副本优化掉?在构造函数中使用非平凡的代码变得不可能;特别是,阅读&amp; var 是避免捷径的好理由。