我正在尝试调用一些函数,包括向vector添加元素(通过value传递的参数):
std::vector<Val> vec;
void fun( Val v )
{
...
vec.push_back(std::move( v ));
...
}
问题是:移动语义有什么好处吗?
我很困惑变量会在新的矢量元素构造时从堆栈“移动”到堆:它似乎只会被复制而已。
答案 0 :(得分:2)
如果std :: move允许底层对象将指针从一个移动到另一个;而不是(在复制中做了什么)分配一个新区域,并复制可能是大量内存的内容。例如
class derp {
int* data;
int dataSize;
}
数据&amp; dataSize可以在移动中交换;但副本可能要贵得多
但是,如果你只有一个整数集合;移动和复制将达到相同的目的;除了旧版本的对象应该无效;无论在该对象的背景下应该意味着什么。
答案 1 :(得分:2)
移动而不是复制是否有任何好处取决于Val
的实施。
例如,如果Val
的复制构造函数的时间复杂度为 O(n),但其移动构造函数的复杂度为 O(1 )(例如:它只包含一些内部指针),然后可能会有性能优势。
在Val
的以下实施中,移动而不是复制不会带来任何性能上的好处:
class Val {
int data[1000];
...
};
但对于下面的那个可以:
class Val {
int *data;
size_t num;
...
};
答案 2 :(得分:1)
移动是指对象的内部存储被盗(如果适用)。可以移动许多标准容器,因为它们在堆上分配存储,这对于“移动”来说是微不足道的。在内部,会发生的事情如下所示。
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
将便宜地将其托管对象移动到vector
新创建的array
。
因此,如果Val
是一个可移动的对象,那么下面这样的函数可以减少开销。
std::vector<Val> vec;
void fun( Val v )
{
...
vec.emplace_back(std::move( v ));
...
}
你无法摆脱构造vector
,在容量填充时分配额外空间或构建Val
内的vector
个对象,但如果{{1}它只是一个指向堆存储的指针,它可以在不实际窃取另一个向量的情况下获得。如果Val
不可移动,则不会因为将其转换为右值而丢失任何内容。关于这种类型的函数签名的好处是调用者可以决定他们是想要复制还是移动。
Val
第一个将调用// Consider the difference of these calls
Val v;
fun(v);
fun(std::move v);
的复制构造函数(如果存在),否则将无法编译。第二个将调用移动构造函数(如果存在),否则调用复制构造函数。如果两者都不存在,它将无法编译。