C++初学者指南-6.函数对象–函数对象
文章目录
- C++初学者指南-6.函数对象--函数对象
- 函数对象
- 示例:区间查询
- 区间内的查找
- 区间划分(分组)
- 指南
- 标准库函数对象
- 比较
- 算术运算
函数对象
提供至少一个成员函数重载 operator() 的对象
class Multiplier {int m_; public:// constructor:explicit constexpr Multiplier (int m) noexcept : m_{m} {}// call operator:constexpr int operator () (int x) const noexcept { return m_ * x; } };
运行示例代码
可以像函数一样使用:
Multiplier triple{3}; int i = triple(2); // i: 6
可以是有状态的:
class Accumulator {int sum_ = 0; public:void operator () (int x) noexcept { sum_ += x; }int total () const noexcept { return sum_; } };
Accumulator acc;
acc(2);
acc(3);
int sum = acc.total(); // sum: 5
可以用来定制行为:
// 比如使用标准库算法: if ( std::any_of(begin(v), end(v), in_interval{-2,8}) ) … // 自定义函数对象 ^
示例:区间查询
class in_interval {int a_;int b_;
public:// constructor:explicit constexprin_interval (int a, int b) noexcept: a_{a}, b_{b} {}// call operator:[[nodiscard]] constexprbool operator () (int x) const noexcept {return x >= a_ && x <= b_;}
};
运行示例程序
// 生成对象
in_interval test {-10,5};
// 调用对象的 operator()
cout << test(1); // true
cout << test(5); // true
cout << test(-12); // false
cout << test(8); // false
区间内的查找
std::vector<int> v {9,0,4,1,8,3,7,2,9};
// 查找符合[6,8]区间到的第一个值
// 在v的子范围内如图所示:
auto i = find_if(begin(v)+2, begin(v)+7, in_interval{6,8});
if (i != end(v)) {auto value = *i; // int value = 8 auto index = distance(begin(v),i); // int index = 4…
}
// 在v的所有元素中,查找符合[-4,4]区间的第一个值:
auto j = find_if(begin(v), end(v), in_interval{-4,4});
if (j != end(v)) {auto value = *j; // int value = 0 auto index = distance(begin(v),j); // int index = 1…
}
运行示例代码
区间划分(分组)
请注意,结果分区中元素的相对顺序不必与原始序列中的顺序相同。
std::vector<int> v {1,4,6,0,2,8,3,5};
auto i = partition(begin(v), end(v), in_interval{-1,2});
// print 1st partition:
for_each(begin(v), i, [](int x){ std::cout << x << ' '; });
// prints 1 2 0'
// print 2nd partition:
for_each(i, end(v), [](int x){ std::cout << x << ' '; });
// prints '6 4 8 3 5'
auto firstOf2ndPart = *i; // 6
auto splitIndex = distance(begin(v),i); // 3
运行示例程序
指南
避免有状态的 operator ()
有状态 = operator() 的当前结果依赖于之前对 operator() 的调用。
(例如,因为成员变量的值都用于计算结果,并且在同一次调用operator()中发生了变化。)
注意:许多(标准)算法并不保证传入的函数对象调用的顺序。特别是对于C++17引入的标准算法的并行版本,情况尤为如此。
这意味着传递一个有状态的函数对象可能会根据特定算法的具体实现和在将其传递给算法之前函数对象的状态而产生不同的结果。
- 后续对operator()的调用应该是相互独立的。
- 最好让 operator() 为 const,也就是完全不改变函数对象的状态。
- 如果你在并行标准算法中使用非常量的 operator()(例如,用于跟踪状态信息),那么一定要确保它是线程安全的。例如,像 I/O 流这样的多线程共享资源必须妥善管理。
标准库函数对象
比较
#include <functional>
std::equal_to
std::not_equal_to
std::greater
std::less
std::greater_equal
std::less_equal
C++11 必须显式指定操作数类型:std::greater<Type>{}
C++14 不需要指定操作数类型:std::greater<>{}
#include <set>
#include <vector>
#include <functional>
#include <algorithm>
// 按降序设置 (默认是“升序”):
std::set<int,std::greater<>> s;
// 与` greater `而不是默认的` less `比较:
std::vector<int> v1 = {1,4,5};
std::vector<int> v2 = {1,2,5};
cout << lexicographical_compare(begin(v1), end(v1),begin(v2), end(v2), std::greater<>{}); // true
运行示例代码
算术运算
#include <functional>
std::plus
std::minus
std::multiplies
std::divides
std::modulus
std::negate
C++11 必须明确指定操作数类型:std::minus<Type>{}
C++14 不需要指定操作数类型:std::minus<>{}
示例:使用二元运算的左折叠
使用运算符 + 作为默认值,如果没有给出折叠操作作为第四个参数⇒结果是输入元素的总和。
#include <vector>
#include <functional>
#include <numeric>
std::vector<int> v {1,2,3,4,5};
// using default (operator +):
int sum = accumulate(begin(v), end(v), 0); // sum 15
// using multiplication:
int product = accumulate(begin(v), end(v), 1, std::multiplies<>{}); // product = 120(1*1*2*3*4*5)
附上原文链接
如果文章对您有用,请随手点个赞,谢谢!^_^