假設給定一個數組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表達式中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的元素。具體差別還要在繼續查查資料。