C++标准库中的算法与应用之一
非修改序列
for_each
- 按顺序将给定的函数对象f应用于解引用范围为[first,last)的每个迭代器的结果。
- 将给定的函数对象f应用于解引用[first,last)范围内的每个迭代器的结果(不一定按顺序)。
- 根据策略执行
对于这两个重载,如果迭代器类型是可变的,则f可以通过取消引用的迭代器来修改范围的元素。如果f返回结果,则忽略该结果。
与其余并行算法不同,for_each不允许复制序列中的元素,即使它们是可复制的。
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int main()
{
vector<int> nums{
31, 14, 20, 28, 35, 27};
int sum = 0;
auto print = [](const int& n) {
std::cout << " " << n; };
cout << "before:";
for_each(nums.cbegin(), nums.cend(), print);
cout << endl;
for_each(nums.begin(), nums.end(), [](int &n){
n++; });
for_each(nums.begin(), nums.end(), [=, &sum](int n){
sum += n;});
cout << "after: ";
for_each(nums.cbegin(), nums.cend(), print);
cout << '\n';
cout << "sum: " << sum << '\n';
}
输出结果
before: 31 14 20 28 35 27
after: 32 15 21 29 36 28
sum: 161
执行策略ExecutionPolicy
如果执行策略类型用作唯一类型,则可以消除并行算法重载的歧义
- 如果要求不得并行执行并行算法。通常指定执行策略为std :: execution :: seq,用此策略调用的并行算法中,访问元素的函数调用,在调用线程中无法确定其顺序。
- 如果要求并行算法的执行可以并行化。通常指定执行策略为std :: execution :: par,使用此策略调用的并行算法中,访问的元素函数调用,可以在调用线程中执行,也可以在库隐式创建的线程中执行,以支持并行算法执行。在同一线程中执行的任何此类调用,它们的相对次序不能确定。
- 指示并行算法的执行可以在线程之间并行化,向量化或迁移。使用此策略调用的并行算法中的元素访问函数的调用,可以在未指定的线程中以无序方式执行,并且在每个线程中彼此之间没有顺序。
- 指示可以对并行算法的执行进行矢量化处理,例如,使用对多个数据项进行操作的指令在单个线程上执行。
在使用这些执行策略中的任何一个,执行并行算法的过程中,如果元素访问函数的调用,通过未捕获的异常退出,则将调用std :: terminate。
parallel_policy策略
使用执行策略
#include <vector>
#include <algorithm>
#include <iostream>
#include <mutex>
#include <execution>
using namespace std;
int main()
{
vector<int> nums{
31, 14, 20, 28, 35, 27};
mutex m;
int sum=0;
auto print = [](const int& n) {
std::cout << " " << n; };
cout << "before:";
for_each(nums.cbegin(), nums.cend(), print);
cout << endl;
for_each(execution::par, nums.cbegin(), nums.cend(),
[&](int n) {
lock_guard<mutex> guard(m);
sum += n;
});
cout << "after: ";
for_each(nums.cbegin(), nums.cend(), print);
cout << '\n';
cout << "sum: " << sum << '\n';
}
输出结果
before: 31 14 20 28 35 27
after: 31 14 20 28 35 27
sum: 155
上述使用了并行执行策略,所以,使用mutex避免数据争用。
sequenced_policy策略
此策略不是并行执行策略:使用它会强制实现,在调用该函数的线程上执行所有操作,因此不存在并行性。但是它仍然是一个执行策略,因此对算法复杂性和异常影响的影响与其他标准策略相同。
不仅所有操作都必须在同一线程上执行,而且必须以一定的顺序执行,因此它们不会交错。确切的顺序是不确定的,并且在函数的不同调用之间可能会有所不同。特别是,如果没有执行策略,则不能保证操作的执行顺序与相应重载的执行顺序相同。例如,对std :: for_each的以下调用将以未指定的顺序填充数字1-1,000的向量。这与没有执行策略的重载形成对比,后者将按顺序存储数字:
count/count_if
返回满足特定条件,在[first,last)范围内的元素的数目。
- 计算等于某个值的元素。
- 计算谓词p返回true的元素。
- 根据策略执行。
#include <algorithm>
#include <iostream>
#include <array>
#include <execution>
using namespace std;
int main()
{
constexpr array<int,10> v = {
1, 2, 3, 4, 4, 3, 7, 8, 9, 10 };
int target1 = 3;
int target2 = 5;
int num_items1 = count(v.begin(), v.end(), target1);
int num_items2 = count(v.begin(), v.end(), target2);
cout << "number: " << target1 << " count: " << num_items1 << '\n';
cout << "number: " << target2 << " count: " << num_items2 << '\n';
int num_items3 = count_if(v.begin(), v.end(), [](int i){
return i % 3 == 0;});
cout << "number divisible by three: " << num_items3 << '\n';
auto distance = [](auto first, auto last) {
return count_if(first, last, [](auto n){
if (n>5) return true; else return false;});
};
cout << "distance: " << distance(v.begin(), v.end()) << endl;
auto num_items4 = count_if(execution::par, v.begin(), v.end