我正在尝试调用一些函数,包括向vector添加元素(通过value传递的参数):
std::vector<Val> vec;
void fun( Val v )
{
...
vec.push_back(std::move( v ));
...
}
问题是:移动语义有什么好处吗?
我很困惑变量将在新的向量元素构造时从堆栈“移动”到堆:它似乎只是被复制而已。
如果std :: move允许底层对象将指针从一个移动到另一个;而不是(在复制中做了什么)分配一个新区域,并复制可能是大量内存的内容。例如
class derp {
int* data;
int dataSize;
}
data&dataSize可以在移动中交换;但副本可能要贵得多
但是,如果你只有一个整数集合;移动和复制将达到相同的目的;除了旧版本的对象应该无效;无论在该对象的背景下应该意味着什么。
移动而不是复制是否有任何好处实际上取决于Val
的实现。
例如,如果Val
的复制构造函数的时间复杂度为O(n),但其移动构造函数的复杂度为O(1)(例如:它只包含一些内部指针),那么可能会有性能优势。
在Val
的以下实现中,移动而不是复制不会有任何性能上的好处:
class Val {
int data[1000];
...
};
但对于下面的那个可以:
class Val {
int *data;
size_t num;
...
};
如果适用,移动是对象的内部存储被盗。可以移动许多标准容器,因为它们在堆上分配存储,这对于“移动”来说是微不足道的。在内部,会发生的事情如下。
template<class T>
class DumbContainer {
private:
size_t size;
T* buffer;
public:
DumbContainer(DumbContainer&& other) {
buffer = other.buffer;
other.buffer = nullptr;
}
// Constructors, Destructors and member functions
}
正如您所看到的,对象不会在存储中移动,“移动”纯粹是从容器other
到this
的概念。实际上,这是优化的唯一原因是因为对象保持不变。
堆栈上的存储无法移动到另一个容器,因为堆栈上的生命周期与当前范围相关联。例如,std::array<T>
将无法将其内部缓冲区移动到另一个阵列,但如果T
在堆上有存储空间,则仍然可以高效移动。例如,从std::array<std::vector<T>>
移动将必须构建vector
对象(array
不可移动),但vector
s将廉价地将其管理对象移动到新创建的vector
的array
s。
因此,如果Val
是一个可移动的对象,那么下面的函数可以减少开销。
std::vector<Val> vec;
void fun( Val v )
{
...
vec.emplace_back(std::move( v ));
...
}
你无法摆脱构造vector
,在容量填充时分配额外空间或在Val
中构建vector
对象,但如果Val
只是指向堆存储的指针,它就像你实际上没有实际得到的那样便宜偷另一个矢量。如果Val
不可移动,你不会丢失任何东西,因为它将它转换为右值。关于这种类型的函数签名的好处是调用者可以决定他们是想要复制还是移动。
// Consider the difference of these calls
Val v;
fun(v);
fun(std::move v);
第一个将调用Val
的复制构造函数(如果存在),否则将无法编译。第二个将调用移动构造函数(如果存在),否则调用复制构造函数。如果两者都不存在则不会编译。