[英]how to use c++11 move semantics to explicitly avoid copying
我有一行代码,其中仅在一次计算中重复使用结构类型的变量,其结果被分配回变量本身。
for (int i = 0; i < 100; i++)
e = f(i,e); //how to avoid copying
整个代码在下面列出。 我的问题是
如何显式确保e
中的数据没有被多次复制。
移动语义是一个不错的选择吗?
与将e
定义为普通的非恒定引用相比,使用move有何不同?
#include <vector>
using namespace std;
struct arr {
vector<int> data;
int len;
};
arr f(int x,arr xs) {
xs.data.push_back(x);
return xs;
}
arr g() {
arr e;
for (int i = 0; i < 100; i++)
e = f(i,e); //how to avoid copying
return e;
}
int main() {
auto res = g(); return 0;
}
-编辑-
最接近我要查找的内容是关于更改为e = f(i,std::move(e))
。 据我了解,它明确地告诉编译器不再需要e
的第二次出现,并且可以占用其资源,我想这是编译器无法推断的,必须告知。
我以这种方式编写示例代码的原因是,我想避免在void f
使用非常量引用,并在不牺牲效率的情况下获得非副作用代码的外观/效果。 我认为这是移动语义为我们带来的好处,对吗? (允许将标准容器“复制”到周围)。 我伪造len
字段和struct
来说明情况,因为stl容器已经具有所有move语义处理程序。
鉴于我到目前为止从答案中学到的东西,我的问题实际上可以归结为:在代码中使用e = f(i,std::move(e))
之前和之后,实际上发生了多少vector
复制操作? 是零还是100? 又为什么呢?
我是否需要将f
声明为arr f(int x,arr&& xs) {
,为什么或为什么不需要使用&&
?
如何显式确保e中的数据没有被多次复制。
通过引用传递arr。
移动语义是一个不错的选择吗?
在这种情况下,这不是一个好的选择。 您将结果分配回e(这样就可以了)。 问题是,如果您在f中执行某些操作会导致失败(引发异常)。 在这种情况下,不仅会丢失所有更改,而且还会丢失所有e
(因为它的值将被移至f
而不分配回去)。
与将e定义为普通的非恒定引用相比,使用move有何不同?
使用移动意味着您可以进行有效的破坏性复制(操作后原件保留为空)。
如果我理解问题,可以使用指针或引用
如何显式确保e中的数据没有被多次复制。
移动语义是一个不错的选择吗?
不,至少在这种情况下不行。 您需要通过引用获取数组:
#include <algorithm>
#include <vector>
using namespace std;
struct arr {
vector<int> data;
int len;
};
void f(arr& a, int x) {
a.data.push_back(x);
}
arr g() {
arr e;
e.data.reserve(100);
for (int i = 0; i < 100; i++)
f(e, i);
return e;
}
int main() {
auto res = g(); return 0;
}
如果您知道元素的数量(示例中为100
),则可能要为它们保留空间,这样可以更有效。
作为对utnapistim的回答的补充,我为您提供了可以实现相同结果的替代方法,即使用C ++ 11语言和库功能用n个连续的整数值填充向量。
考虑到您显然希望将一个vector
和一个似乎要存储数组大小的int
分组的事实,我可以想出的最短版本是
#include <vector>
#include <algorithm>
struct arr {
std::vector<int> data;
int len;
};
int main() {
arr a;
std::generate_n(std::back_inserter(a.data), 100, [&]() -> int { return a.data.size (); });
return 0;
}
没有办法解决arr::data
的构造。 假设您无法估计要推送的元素数量,那么在推送之前调整其大小也不是一种选择。 尽管如此,您仍可以使generate_n
只是push_back
(通过使用std::back_inserter
)由生成器函数生成的n个值(如您所见,可以是一个整洁的小lambda)。 有效地,您可以大大减少代码和问题。
上面的解决方案避免了原始代码的多个问题:
a)您不必调用不必要的函数f()
-它只需要执行push_back
即可直接在g()
编写。 定义和调用f()
完全是没有必要的,但是最糟糕的部分是每次您要push_back一个整数时都复制arr
。 这意味着您每次都必须构造一个新vector
并复制原始vector
任何内容,这意味着您要在向量中存储任何类型的n个构造。
b)您不必编写显式循环并手动调用push_back
。 这使得函数g()
不再需要。
c)最后,随后避免使用函数g()
。 使用g()
的返回值初始化res
通常不是问题,因为返回值优化消除了在g()
复制本地vector
的需要(这正是g++
在调用g()
初始化res
时所做的工作) 。 但是,与性能优化相比,这更多的是代码减少-您要编写和维护的功能要少得多。
每个qwm对问题的评论,使用
e = f(i,std::move(e));
会为您节省一份,而实际上还有两份预期的副本正在移动。
从g返回还有一个动作。
但正如其他答案所述,请改用引用传递。 或者直接使用向量而不是结构(向量知道它们自己的长度)。
顺便说一句,如果您事先知道向量将包含多少个元素,则只需设置向量的大小并使用循环就可以像分配数组一样分配元素。 由于每个调用都要进行容量检查,因此使用push_back会产生开销。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.