本章主要内容:
一,函数对象
二,标准库中的std::function模板
三,参考阅读
一,函数对象
1.函数对象的概念
函数对象的用法如下:
//class可以换成struct
class FunctionObjName
{
public:
ReturnType operator()(ParamType1, ... , ParamTypeN){
process code
}
};
2.函数对象的应用
使用函数对象的步骤:
step.01: 新建一个类,在类中定义一个重载的函数调用运算符
class Less
{
public:
bool operator()(int a,int b) const;
};
//Less类的成员函数:函数调用运算符operator()
bool Less::operator()(int a, int b) const
{
return a < b;
}
Less less_obj;
const bool_is_less = less_obj(5, 6);
3.标准库中的函数对象
STL标准库中提供了很多函数对象的类模板,它们都包含在头文件functional中。
例如上面提到的Less类,可以使用标准库中的”std::less<int>less”。从C++14标准开始,可以省略类型实参,例如”std::less<>less”。
标准库中常见的函数对象:
//方式一,直接调用
std::cout << std::plus<int>()(4, 5) << std::endl;
//方式二,实例化一个新的类,然后调用
std::plus<int> plus_obj;
std::cout << plus_obj(4, 5) << std::endl;
4.函数对象的传参
#include <cmath>
class Nearer
{
public:
Nearer(int value):{
n=value;
}
bool operator()(int a,int b) const {
return std::abs(a - n) < std::abs(b - n);
};
private:
int n;
};
5.C++代码样例
#include <iostream>
#include <vector>
#include <cmath> //For std::abs()
//用于对vector中逐个元素进行操作的模板函数
template <typename T, typename Process_type>
const T* find_optimum(const std::vector<T>& values, Process_type process)
{
if (values.empty())
return nullptr;
const T* res = &values[0];
for (size_t i = 1; i < values.size(); ++i)
{
if (process(values[i], *res))
{
res = &values[i];
}
}
return res;
}
class Nearer
{
public:
Nearer(int value): n(value){ }
//重载函数调用运算符
bool operator()(int a, int b) const {
return std::abs(a - n) < std::abs(b - n);
};
private:
int n;
};
class Less
{
public:
//重载函数调用运算符
bool operator()(int a, int b) const {
return a < b;
}
};
int main()
{
Less less_obj;
std::vector<int> numbers{ 23, 18, 17, 66, 40, 50 };
std::cout << "Minimum element: " << *find_optimum(numbers, less_obj) << std::endl;
std::cout << "The number nearest 36 is: " << *find_optimum(numbers, Nearer(50)) << std::endl;
}运行结果:
Minimum element: 17
The number nearest 36 is: 50
Demo_2:
#include <iostream>
#include <vector>
#include <algorithm>
class MeanValue {
private:
int num;
int sum;
public:
MeanValue() : num(0), sum(0) {
}
//function call
void operator()(int elem) {
++num;
sum += elem;
}
double get_mean_value() {
return static_cast<double>(sum) / static_cast<double>(num);
}
};
int main()
{
std::vector<int> data_list = { 1, 2, 3, 4, 5, 6, 7, 8};
MeanValue mv_obj = std::for_each(data_list.begin(), data_list.end(), MeanValue());
std::cout << "mean value: " << mv_obj.get_mean_value() << std::endl;
}运行结果: mean value: 4.5
二,标准库中的std::function模板
1.std::function简介
std::function<>是C++11标准引入的类模板。
std::function<>专门用来包装可调用的函数对象。
在"<>"里面传入返回值类型和传参类型就可以开始使用std::function<>了。
std::function<>用法如下: std::function<ReturnType(ParamType1, ... , ParamTypeN)>
std::function<>类模板的特点是,可以通过指定的类型参数,来统一处理设定返回值类型和参数类型
的各种函数对象。
std::function<int(int)> 可以用来专门调用返回值是int类型,形参是int类型的函数对象。
因此,有了std::function<>,不同实现的各种函数对象可以共用同一种调用形式(call signature)。
实例化以后的std::function<>,例如std::function<int(int)>,可以被理解为是某种特定调用形式的一个容器。
2.std::function具体用法
std::function<>被实例化以后可以调用: 普通函数 函数对象 lambda表达式。 用法演示: 应用场景:std::function<int(int, int)> 如下定义了返回值为int类型,传参为(int, int)的三种实现方式: add -->普通函数实现 mod -->lambda表达式实现 divide -->函数对象实现(struct某种程度上用法和对象一样) int add(int i, int j){return i + j;}
auto mod = [](int i, int j){return i % j;};
struct divide
{
int operator()(int m, int n)
{
return m / n;
}
};std::function调用它们的方式如下: //初始化
std::function<int(int, int)> f1 = add;
std::function<int(int, int)> f2 = divide();
std::function<int(int, int)> f3 = mod;
//调用
std::cout << f1(4, 2) << std::endl;
std::cout << f2(4, 2) << std::endl;
std::cout << f3(4, 2) << std::endl;完整C++代码实现: #include <iostream>
运行结果:
#include<functional>
int add(int i, int j) { return i + j; }
auto mod = [](int i, int j) {return i % j; };
struct divide
{
int operator()(int m, int n)
{
return m / n;
}
};
int main()
{
std::function<int(int, int)> f1 = add;
std::function<int(int, int)> f2 = divide();
std::function<int(int, int)> f3 = mod;
std::function<int(int, int)> f4 = [](int i, int j) {return i * j; };;
std::cout << f1(4, 2) << std::endl;
std::cout << f2(4, 2) << std::endl;
std::cout << f3(4, 2) << std::endl;
std::cout << f4(4, 2) << std::endl;
}6
2
0
83.C++代码样例
Demo_1: #include <functional>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
void execute(const vector<function<void()>>& fs)
{
for (auto& f : fs)
f();
}
void plain_old_func()
{
cout << "I'm an old plain function" << endl;
}
class functor
{
public:
void operator()() const
{
cout << "I'm a functor" << endl;
}
};
int main()
{
vector<function<void()>> x;
x.push_back(plain_old_func);
functor functor_instance;
x.push_back(functor_instance);
x.push_back([]()
{
cout << "I'm a lambda expression" << endl;
});
execute(x);
}运行结果: I'm an old plain function
I'm a functor
I'm a lambda expressionDemo_2: #include <iostream>
#include <functional>
int main() {
// an array of functions:
std::function<int(int, int)> fn[] = {
std::plus<int>(),
std::minus<int>(),
std::multiplies<int>()
};
for (auto& x : fn) {
std::cout << x(10, 5) << 'n';
}
return 0;
}运行结果: 15
5
50*补充:头文件functional在C++17标准中引入了std::invoke。 invoke可以不需要经过初始化操作,直接进行调用操作。 std::invoke具体使用方式参考如下代码: #include <iostream>
#include <functional>
using namespace std;
void globalFunction()
{
cout << "globalFunction ..." << endl;
}
class MyClass
{
public:
void memberFunction(int data)
{
std::cout << "MyClass memberFunction ..." << std::endl;
}
static void staticFunction(int data)
{
std::cout << "MyClass staticFunction ..." << std::endl;
}
};
int main()
{
MyClass obj;
std::invoke(&MyClass::memberFunction, obj, 100);
std::invoke(&MyClass::staticFunction, 200);
std::invoke(globalFunction);
return 0;
}运行结果: MyClass memberFunction ...
MyClass staticFunction ...
globalFunction ...三,参考阅读
《Beginning C++17, 5th Edition》
《C++ Primer Plus, 6th Edition》
《The C++ Standard Library, Second Edition》
《C++新经典》
C/C++开发基础——函数指针&回调函数
https://www.oreilly.com/library/view/mastering-
c-programming/
https://oopscenities.net/2012/02/24/c11-
stdfunction-and-stdbind/
原文始发于微信公众号(程序员与背包客):C/C++开发基础——函数对象与std::function模板
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/128587.html