C++ 函数调用运算符与可调用对象

函数调用运算符

函数调用运算符用“operator ()”表示。如果类重载了函数调用运算符,可以像使用函数一样使用该类对象。
例如,像下面一样重载了某个struct或class的operator(),其对象就能像函数一样调用。

// 类重载operator()
struct absInt { // class一样可以
	int operator()(int val) const { // 定义运算符operator(), 求val绝对值
		return val < 0 ? -val : val; 
	}
	// ... 其他成员函数或者成员变量声明(如果有的话)
};

// “调用”该类对象
absInt obj; // 函数对象
int res = obj(-10); // 传递参数10给obj.operator(), res值为10

[======]

函数对象

函数对象基本概念

如果类定义了operator()(函数调用运算符),那么这样的类的对象称为函数对象(function object),其调用行为类似于调用函数。
函数对象跟普通对象没有本质区别,也可以有自己的成员变量和成员函数。

例如,定义class PrintString

// 定义函数对象对应的类
class PrintString {
public:
	PrintString(ostream& o = cout, char c = ' ') : os(o), seperator(c) { }
	void operator()(const string &s) const { os << s << seperator; } // 定义operator()
	~PrintString(){}
private:
	ostream &os; // 输出流
	char seperator; // 不同输出分隔符
};

/* 使用函数对象 */
string s = "test";
PrintString printer; // 定义函数对象, 使用构造函数默认值, 打印到cout, 分隔符为' '
printer(s); 
PrintString errors(cerr, '\n'); // 定义函数对象, 打印到cerr, 分隔符'\n'
errors(s);

// 打印一个数组
for_each(vec.begin(), vec.end(), PrintString(cerr, '\n'));

[======]

lambda与函数对象

lambda本质是匿名函数对象,其产生的类中含有一个重载的函数调用运算符。
例如,对vec数组进行排序。下面的lambda表达式[](const int a, const int b) { return a < b;} 其实是一个函数对象

#include <algorithm>

vector<int> vec;
// ... // 往vec添加数据
// lambda表达式
stable_sort(vec.begin(), vec.end(), [](const int a, const int b) {
	return a < b;
});

// <=> 等价于(函数指针)
stable_sort(vec.begin(), vec.end(), compareFunc);

bool compareFunc(int a, int b)
{
	return a < b;
}

// <=> 等价于(函数对象)
struct Compare
{
	bool operator()(int a, int b)
	{
		return a < b;
	}
};

Compare cobj;
stable_sort(vec.begin(), vec.end(), cobj);

[======]

lambda值捕获与函数对象

通过值捕获的局部变量,在lambda对应类中,相当于private数据成员。

// 在words数组中, 找到第一个满足条件(lambda表示返回true)的元素的迭代器. 条件是对应元素的size() >= sz
auto wc = find_if (words.begin(), words.end(), [sz](const string& a) {
	return a.size() >= sz;
});

// 该lambda对应类
class SizeComp {
public:
	SizeComp(size_t n) : sz(n) { }
	bool operator()(const string& s) const {
		return s.size() >= sz;
	}
private:
	size_t sz;
};

[======]

标准库定义的函数对象

标准库已经定义了一组表示算术运算符、关系运算符、逻辑运算符的类,每个类各自定义了一个执行类名称对应操作的运算符。
比如,plus类定义了operator()用于加法运算;modulus类定义了opeartor()用于求%运算;equal_to类定义了operator()用于判断2个参数是否相等,等等。
这些类都被定义成了模板形式,例如,其使用方法见下:

#include <functional>

// 定义函数对象
plus<int> intAdd; // 可执行加法运算的函数对象
negate<int> intNegate; // 可对int值加上负号的函数对象
greater<int> comp1; // 执行比较运算的函数对象
less<int> comp2; // 执行比较运算的函数对象

// 调用函数对象
int sum = intAdd(1,2); // 使用intAdd::operator(int,int)对1和2求和, sum值为3
sum = intNegate(intAdd(3,4)); // sum值为-7
bool res1 = comp1(1,2); // false
bool res2 = comp2(1,2); // true

在算法中使用标准库函数对象

比如,使用sort进行排序

vector<string> vec;
// ... 向vec插入数据
// 传入临时函数对象, 用于2个string对象的 > 比较运算
sort(vec.begin(), vec.end(), greater<string>()); // 降序排序
// 传入临时函数对象, 用于2个string对象的 < 比较运算
sort(vec.begin(), vec.end(), less<string>());    // 升序排序

可调用对象与function

C++可调用的对象包括:函数、函数指针、lambda表达式、bind创建的对象、重载了函数调用运算符(operator())的类。

  • 调用形式
    可调用的对象也有自己的类型,2个不同类型的可调用对象可能共享一种调用形式(call signature)。一种调用形式对应一个函数类型,指明了返回类型、参数列表,如:
int (int, int)
返回类型为int;参数列表int,int,即接受2个int参数。
  • 不同类型可调用参数,具有相同的调用形式
    多种不同类型的可调用参数,可能具有相同的调用形式。例如,
// 普通函数
int add(int i, int j) { return i + j; }
// lambda表达式
auto s = [](int i, int j) -> int{ return i + j; }
// 函数对象类
struct sum {
	int operator()(int i, int j) {
		return i + j;
	}
};

标准库function类型

在C中,当需要一个函数指针时,可以直接传函数或者函数指针,但是无法传lambda表达式或者函数对象类,因为后者并不是函数指针类型。
C++中的function能解决这个问题,代表任意一种C++可调用对象。
function是模板,当创建一个具体的function类型时,必须提供额外信息。

// C的使用函数指针的做法
typedef int (*AddFunc)(int , int);
AddFunc handle = add;
int s = handle(1,2); // 3

// C++使用function的做法
function<int(int, int)> f1 = add; // 函数指针
function<int(int, int)> f2 = sum(); // 函数对象
function<int(int, int)> f3 = [](int i, int j){ return i + j; }; // lambda

int s1 = f1(3, 4); // 7
int s2 = f2(3, 4); // 7
int s3 = f3(3, 4); // 7

[======]

posted @ 2021-11-23 00:14  明明1109  阅读(481)  评论(0编辑  收藏  举报