函数调用运算符

如果类重载了函数调用运算符,则可以像使用函数一样使用该类的对象,因为这样的类同时也能存储状态,所以与普通函数相比它们更加灵活。

struct absInt{
	int operator()(int val) const{
		return val < 0 ? -val : val;
	}
};

上面的类只定义了一种操作:函数调用运算符,它负责接受一个int类型的形参,然后返回该实参的绝对值。

int i = -42;
absInt absObj;
int ui = absObj(i);	//i被传递给absObj.operator()

即使 absObj是一个对象而非函数,也能调用该对象,调用对象实际上是在运行重载的调用运算符。

函数调用运算符必须是成员函数,一个类可以定义多个不同版本的调用运算符,相互之间在参数数量和参数类型上应该有所区别。

如果类定义了调用运算符,则该类的对象称作函数对象,因为可以调用这种对象,所以这些对象的行为像函数一样。

含有状态的函数对象类

函数对象除了 operator() 之外也可以包含其他成员。

class PrintString{
public:
	PrintString(ostream &o = cout,char c = ' '):os(o),sep(c){ }
	void operator()(const string &s)const{os<<s<<sep;}
private:
	ostream &os;
	char sep;
};

使用:

PrintString printer;
printer(s);		//cout中打印s,后面跟一个空格
PrintString errors(cerr,'
');
errors(s);		//cerr中打印s,后面跟一个换行符

函数对象通常是作为泛型算法的实参:

for_each(vs.begin(),vs.end(),PrintString(cerr,"
"));

lambda 是函数对象

编写了一个 lambda 后,编译器将该表达式翻译成一个未命名类的未命名对象。

stable_sort(words.begin(),words.end(),[](const string &a,const string &b)
{return a.size() < b.size();});

其行为类似下面这个类的一个未命名对象:

class ShorterString{
public:
	bool operator()(const string &a,const string &b)
	{return a.size() < b.size();}
};

使用上面的类重写 stable_sort :

stable_sort(words.begin(),words.end(),ShorterString());

表示 lambda 及相应捕获行为的类

auto wc = find_if(words.begin(),words.end(),[sz](const string &a)
	{return a.size() > = sz;})

该 lambda 表达式产生的类将形如:

class SizeComp
{
	SizeComp(size_t n):sz(n) { }
	bool operator()(const string &s)const {return s.size() >= sz;}
private:
	size_t sz;
};

auto wc = find_if(words.begin(),words.end(),SizeComp(sz))

标准库定义的函数对象

标准库定义了一组表示算术运算符、关系运算符和逻辑运算符的类,每个类分别定义了一个执行命名操作的调用运算符。

plus<int> intAdd;	//可执行int加法的函数对
negate<int> intNegate;	//可对int值取反的函数对象
int sum = intAdd(10,20);	//sum=30
sum = intAdd(10,intNegate(10));	//sum=0

定义在 functional 头文件中的函数对象:

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

// 传入一个临时函数对象用于执行两个 string 对象的比较运算
sort(svec.begin(),svec.end(),greater<string>());

标准库规定其函数对象对于指针同样适用:

vector<string *> nameTable; //指针的vector
//错误:nameTable 中的指针彼此之间没有任何关系,所以 < 将产生未定义的行为
sort(nameTable.begin(),nameTable.end(),[](string *a,string *b){return a < b;}) ;
//正确,标准库规定指针的 less 定义是良好的
sort(nameTable.begin(),nameTable.end(),less<string*>());

可调用对象与 function

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

可调用对象也有类型,labmda 有自己唯一的未命名类型,函数及函数指针的类型由其返回值类型和实参类型决定。两个不同类型的可调用对象却可能共享同一种调用形式,调用形式指明了返回类型以及传递给调用的实参类型,一种调用形式对应一个函数类型:

int (int,int)	//是一个函数类型,它接受两个int,返回一个int

不同的类型可能具有相同类型的调用方式

int add(int i,int j){return i + j;}
auto mod = [](int i,int j){return i % j;}
struct divide{
	int operator()(int denminator,int divisor){
		return denminator / divisor;
	}
};


尽管这些可调用对象对其参数执行了不同的算术运算,尽管它们的类型各不相同,但是共享一种调用形式:

int (int,int)

构建实现不同运算的函数表:

map<string,int(*)(int,int)> binops;
binops.insert({"+",add});

binops.insert({"%",mod});	//错误,mod不是函数指针

标准库 function 类型

function 定义在 functional 头文件中:

function 是一个模板,创建具体的 function 类型时需要提供额外的信息。

function<int(int,int)>

function<int(int,int)> f1 = add;		//函数指针
function<int(int,int)> f2 = divide;		//函数对象类的对象
function<int(int,int)> f3 = [](int i,int j){return i * j;}		//lambda

//调用
cout<<f1(4,2)<<endl;
cout<<f2(4,2)<<endl;
cout<<f3(4,2)<<endl;

使用 function 重新定义上面的函数表:

map<string,function<int(int,int)>> binops = 
{
	{"+",add},		//函数指针
	{"-",std::minus<int>()},		//标准库函数对象
	{"/",divide},		//自定义函数对象
	{"*",[](int i,int j){return i * j;}},			//未命名lambda表达式
	{"%",mod},				//命名lambda表达式
};

调用:

binops["+"](10,5);
binops["-"](10,5);
binops["/"](10,5);
binops["*"](10,5);
binops["%"](10,5);

重载的函数与 function

不能直接将重载函数的名字存入 function 类型的对象中。

int add(int i,int j){return i + j;}
Sales_data add(const Sales_data&,const Sales_data&);
map<string,funciton<int(int,int)>> binops;
binops.insert({"+",add});		//错误,不能区分是哪个add 

解决上面问题的有效途径是存储函数指针而非函数名字:

int (*fp) (int,int) = add;
binops.insert("add",fp);	//正确,fp指向正确的add版本

同样,也可以使用 lambda 来指定希望使用的 add 版本:

binops.insert({"+"},[](int a,in b){return add(a,b);});
原文地址:https://www.cnblogs.com/xiaojianliu/p/12496502.html