我有一条线,其中结构类型变量仅在一次计算中重复使用,其结果将被分配回变量本身。
for (int i = 0; i < 100; i++)
e = f(i,e); //how to avoid copying
下面列出了整个代码。我的问题是,
如何明确确保e
内的数据不会多次复制。
移动语义是一个不错的选择吗?
如何使用move比较将e
定义为普通的非常量引用?
#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
中使用非const引用,并且在不牺牲效率的情况下获得非副作用代码的良好外观/错觉。我认为移动语义给我们带来了一件事,对吧? (允许标准容器'复制'arround)。我伪造了len
字段和struct
来创建一个案例,因为stl容器已经拥有所有移动语义处理程序。
鉴于我迄今为止从答案中学到的东西,我的问题实际上归结为:
在代码中使用e = f(i,std::move(e))
之前和之后,实际发生了多少vector
个复制操作?是没有还是100?为什么?
我是否需要将f
声明为arr f(int x,arr&& xs) {
,为什么或为什么我不需要使用&&
?
答案 0 :(得分:3)
如何明确确保e内的数据不会多次复制。
通过引用传递arr。
移动语义是一个不错的选择吗?
在这种情况下,这不是一个好的选择。您将结果分配回e(因此它可以工作)。问题是如果你在f中做了一些会导致失败的事情(抛出异常)。在这种情况下,您不仅会丢失所有更改,而且还会丢失所有e
(因为它的值会移到f
但不会被分配回来)。
如何使用move比较将e定义为普通的非常量引用?
使用移动意味着您可以制作高效的破坏性副本(操作后原件保留为空)。
答案 1 :(得分:1)
如果我理解了这个问题,你可以使用指针或参考
答案 2 :(得分:0)
如何明确确保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
),您可能希望为它们保留空间,这样会更有效。
答案 3 :(得分:0)
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()
完全没必要,但最糟糕的是,每次要复制一个整数时,都要复制arr
。这意味着每次和复制原始版本中的任何内容时都必须构建一个新的vector
,这意味着您在向量中存储的任何类型的n个结构。
b)您不必编写显式循环并手动调用push_back
。这使得函数g()
变得不必要。
c)最后,随后,您将避免对g()
的函数调用。使用返回值res
初始化g()
通常不是问题,因为返回值优化无需复制vector
中的本地g()
(这正是调用g++
初始化g()
时res
的作用。但是,这更多的是代码减少而不是性能优化 - 您可以减少编写和维护的功能。
答案 4 :(得分:0)
根据qwm对该问题的评论,使用
e = f(i,std::move(e));
会为您保存一份副本,而另外两份预期副本实际上会移动。
还有一个从g返回的举动。
但正如其他答案所述,请使用pass-by-reference。或者直接使用向量而不是结构(向量知道它们自己的长度)。
顺便说一句,如果你事先知道你的矢量将包含多少元素,只需设置矢量的大小并使用循环来分配元素,就像你将数组一样。由于每次调用时的容量检查,使用push_back会产生开销。