假设给定一个数组vector<double> veca
以及对应的掩膜(即指示标志数组)vector<bool> flags
,获得veca中对应flags
中为true
的元素。
假设veca
为{0.1, 0.2, 0.3, 0.4}
,flags
为{true, false, false, true}
,则vecb
应该为{0.1, 0.4}
。
使用for循环自然可以很简单地解决这个问题,但是想要用标准模板库中的算法实现需要使用copy_if
。使用copy
算法的复制拷贝效率要比for的效率要高一些。
copy_if
的由四个参数,前两个是输入元素的迭代器,拷贝两个迭代器之间的元素,第三个将元素拷贝到的位置,第四个是选择条件,即只拷贝改条件返回true的元素。
#include <vector>
#include <algorithm>
#include <iostream>
#include <numeric> // accumulate
#include <iterator>
int main()
{
std::vector<bool> flags{ true, true, false, true};
std::vector<double> veca{ 0.1, 0.2, 0.3, -0.1 };
std::vector<double> vecb;
vecb.reserve(std::accumulate(flags.begin(), flags.end(), 0));
// vecb在reserve之后并没有未元素分配内存,插入应该使用back_inserter(vecb)
// vecb在resize之后为元素分配了内存,使用back_inserter(vecb)会在已经分配内存的元素之后插入
// 这时应该使用 vecb.begin(),对已经分配的内存进行覆盖
size_t i = 0;
std::copy_if(veca.begin(), veca.end(), std::back_inserter(vecb),
[&i, &flags](double a){return flags[i++]; });
for (auto &s : vecb)
std::cout << s << std::endl;
// i = 0;
// std::vector<double> vecc;
// std::remove_copy_if(veca.begin(), veca.end(), std::back_inserter(vecc),
// [&i, &flags](double a){return flags[i++]; });
// std::cout << "veca
";
// for (auto& s : veca)
// std::cout << s << " ";
// std::cout << "
vecc
";
// for (auto& s : vecc)
// std::cout << s << " ";
return 0;
}
使用lambda表达式作为第四个元素,捕获掩膜数组flags
和元素序号i
。
注意lambda表达式中flags
和i
都是使用的引用捕获,对于数组等数据结构要使用引用捕获,而且我们希望在lambda表达式中改变i
的值,因此i
也要是用引用捕获。
关于C++ lambda表达式更加详细的说明可参考博客和博客。
要注意的是reserve
与resize
的不同。
reserve
只调整数组的capacity,并不分配元素分配内存,因此需要则copy_if
的第三个参数使用back_insterter
;resize
为元素分配好了空间,如果在copy_if
的第三个参数使用back_inserter
,则会在已经分配内存的元素后面插入,正确地做法是使用vecb.begin()
,对已经分配内存的元素进行覆盖。
remove_copy_if
感觉和copy_if
差不多,就是逻辑相反,使用remove_copy_if
会在第三个参数的位置上收集到第四个参数返回false的元素。具体差别还要在继续查查资料。