C++ 使用copy_if获得数组vector掩膜

假设给定一个数组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\n";
// 	for (auto& s : veca)
// 		std::cout << s << "\t";
// 	std::cout << "\nvecc\n";
// 	for (auto& s : vecc)
// 		std::cout << s << "\t";
	return 0;
}

使用lambda表达式作为第四个元素,捕获掩膜数组flags和元素序号i
注意lambda表达式中flagsi都是使用的引用捕获,对于数组等数据结构要使用引用捕获,而且我们希望在lambda表达式中改变i的值,因此i也要是用引用捕获。
关于C++ lambda表达式更加详细的说明可参考博客博客

要注意的是reserveresize的不同。

  • reserve只调整数组的capacity,并不分配元素分配内存,因此需要则copy_if的第三个参数使用back_insterter
  • resize为元素分配好了空间,如果在copy_if的第三个参数使用back_inserter,则会在已经分配内存的元素后面插入,正确地做法是使用vecb.begin(),对已经分配内存的元素进行覆盖。

remove_copy_if感觉和copy_if差不多,就是逻辑相反,使用remove_copy_if会在第三个参数的位置上收集到第四个参数返回false的元素。具体差别还要在继续查查资料。

posted @ 2018-11-15 09:13  骏腾  阅读(1998)  评论(0编辑  收藏  举报