函数调用运算符

Posted xiaojianliu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数调用运算符相关的知识,希望对你有一定的参考价值。

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

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);});

以上是关于函数调用运算符的主要内容,如果未能解决你的问题,请参考以下文章

调用模板化成员函数:帮助我理解另一个 *** 帖子中的代码片段

php 一个自定义的try..catch包装器代码片段,用于执行模型函数,使其成为一个单行函数调用

如何将数据从回收器适配器发送到片段 |如何从 recyclerview 适配器调用片段函数

如何测量代码片段的调用次数和经过时间

如何从片段 KOTLIN 中调用意图 [重复]

如何让片段中的多个视图调用片段类中声明的相同 onClick 函数?