emplace_back 当循环同一个列表
Posted
技术标签:
【中文标题】emplace_back 当循环同一个列表【英文标题】:emplace_back when loop of the same list 【发布时间】:2013-01-18 13:02:06 【问题描述】:我向你展示我的问题
我有 2 个列表,分别命名为 A 和 B。
list<vector<int> > A = 1,2,3;
list<vector<int> > B = 4,5,6;
我想要的是 A = 1,4,1,5,1,6,2,4,2,5,2,6,3 ,4,3,5,3,6 不使用任何 tmp 列表。
我在 Ubuntu 12.04 上使用 C++11 和 gcc 4.6.3
这样最小化代码:
auto A_begin = A.begin();
auto A_end = A.end();
auto B_begin = B.begin();
auto B_end = B.end();
for(auto i = A_begin; i != A_end; ++i) //loop on A
for (auto j = B_begin;j != B_end; ++j) //loop on B
vector<int> tmp = (*i); // A[i]
copy((*j).begin(),(*j).end(),back_inserter(tmp)); // append B[j] to A[i]
A.emplace_back(tmp); //add it to A
A.erase(A_begin,A_end); // remove 1,2,3
所以,我认为算法没问题,但它会在 A 上产生无限循环。 我认为是当我制作 A.emplace_back 时 A_end 发生了变化,但我保存了它,所以我真的不知道在这里追加什么。
我的代码来识别问题:
auto A_begin = A.begin();
auto A_end = A.end();
auto B_begin = B.begin();
auto B_end = B.end();
int ii = A.size();
for(auto i = A_begin; i != A_end; ++i) //loop on A
for (auto j = B_begin;j != B_end; ++j) //loop on B
vector<int> tmp = (*i);
A.emplace_back(tmp);
cout<<--ii<<endl; // exit when print 0 ?
这个打印负数,我又要^C了。
编辑:我找到了解决方案:
auto A_begin = A.begin();
auto A_end = A.end();
auto B_begin = B.begin();
auto B_end = B.end();
list<vector<int>> tmp_l;
for(auto i = A_begin; i != A_end; ++i) //loop on A
for (auto j = B_begin;j != B_end; ++j) //loop on B
vector<int> tmp = (*i); // A[i]
copy((*j).begin(),(*j).end(),back_inserter(tmp)); // append B[j] to A[i]
tmp_l.emplace_back(move(tmp)); //add it to A
swap(tmp_l,A);
【问题讨论】:
【参考方案1】:这两行:
vector<int> tmp = (*i); // A[i]
copy((*j).begin(),(*j).end(),tmp.end()); // append B[j] to A[i]
将调用未定义的行为。通过复制到 tmp.end(),您只是在 A[i] 结束后覆盖内存,而不是扩展 A[i]。您需要使用 back_insert 迭代器,例如:
vector<int> tmp = (*i); // A[i]
copy((*j).begin(), (*j).end(), back_inserter(tmp)); // append B[j] to A[i]
您还需要包含标题以获取 back_inserter。
编辑:此外,A_end 迭代器指向列表的“结束”位置,因此无论您添加多少项,它们总是添加到 A_end 前面,因此是无限循环。我不确定是否有解决此问题的好方法。不创建临时列表没有任何好处,无论哪种方式,您都在分配相同的内存,只需写入一个新列表即可。
【讨论】:
感谢 back_inserter,但没有变化:/ 这正是我所怀疑的。但是,tmp 列表的副本会很长(我里面有很多对象),所以我尽量不复制它。 tmp 列表不是副本,而是列表。将输出写入一个新列表,然后使用 std::swap 交换 A 和 tmp 的内容。这是一个非常便宜的操作,它不会复制内容,只需移动几个指针。然后当你的函数退出时 tmp 将包含 A 的旧内容并被破坏。 我添加了一个编辑来发布解决这个问题的解决方案,没有 tmp。【参考方案2】:你的算法不好。
这个:
copy((*j).begin(),(*j).end(),tmp.end());
会导致各种问题,因为你覆盖了一些随机内存。
你可能想做这样的事情来追加:
vector<int> tmp = (*i);
copy((*j).begin(),(*j).end(),std::back_inserter(tmp));
【讨论】:
谢谢,但是没有usign copy,我也有同样的问题。 呼叫保留将不起作用。复制完成后,向量的大小不会更新,因此您仍然要写到向量的末尾。【参考方案3】:编辑:我找到了解决方案:
该解决方案很好,使用临时向量并将其与 A
交换比在原地执行它更好,因为您的原始版本(以及复制到向量末尾)以 erase
结尾,它移动 每个 元素。
但您的解决方案可以改进:
// get rid of these iterators, they're useless
/*
auto A_begin = A.begin();
auto A_end = A.end();
auto B_begin = B.begin();
auto B_end = B.end();
*/
list<vector<int>> tmp_l;
// use new for loops
for (auto& a : A)
for (auto& b : B)
// use auto
auto tmp = a; // A[i]
// I find just inserting at the end simpler than using `back_inserter`
tmp.insert(tmp.end(), b.begin(), b.end()); // append B[j] to A[i]
// then clear it the moved-from elements:
b.clear();
// move the tmp vector into place, do not copy it.
tmp_l.emplace_back(std::move(tmp));
swap(tmp_l,A);
【讨论】:
很好,使用 move 是个好主意,但是 b 在循环内,所以,它是第一个 a,而不是其他的。以上是关于emplace_back 当循环同一个列表的主要内容,如果未能解决你的问题,请参考以下文章
为什么emplace_back比push_back更快?快是有条件的
std::vector emplace_back 可以从向量本身的元素复制构造吗?