C++可调用对象与function

C++语言有几种可调用对象:函数、函数指针、lambda表达式、bind创建的对象以及重载了函数调用运算符的类。

和其他对象一样,可调用的对象也有类类型。例如,每个lambda有它自己唯一的(未命名)类类型;函数及函数指针的类型则由其返回值类型和实参类型决定。

然而,两种不同类型的可调用对象却可能共享同一种调用形式。调用形式指明了调用返回的类型以及传递给调用的实参类型。一种调用形式对应一个函数类型,例如:

int(int, int);

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

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

对于几种可调用对象共享同一种调用形式的情况,有时我们希望把它们看成具有相同的类型。例如:

//普通函数
int add(int i, int j) { return i + j; }
//lambda,其产生一个未命名的函数对象类
auto mod = [](int i, int j) { return i % j; }
//函数对象类
struct divide{
    int operator()(int denominator, int divisor)
    {
        return denominator / divisor;
    }
};

它们的类型尽管个不相同,但是共享同一种调用形式:

int(int, int)

我们可能希望使用这些可调用对象构建一个简单的桌面计算器,为了实现这一目的,需要定义一个函数表用于存储指向这些可调用对象的“指针”,当程序需要执行某个特定的操作时,就从该表查找该调用的函数。

函数表通过map实现,使用一个string作为关键字,使用实现运算符的函数作为值。

map<string, int(*)(int, int)> binops;

我们可以按照下面的形式将add添加的指针添加到binops中:

//正确:add是一个指向正确类型函数的指针
binops.insert({"+", add}); //{"+", add}是一个Pair

但我们不能将mod或者divide存入binops,因为mod不是一个函数指针。

标准库function类型

我们可以使用一个名为function的新的标准库类型解决上述问题,function定义在functional头文件中,下表列举出function定义的操作。

                                                           function的操作
function<T> f;f是一个用来存储可调用对象的空function,这些可调节对象的调用形式应该与函数类型T相同(T是retType(args))
function<T> f(nullptr);显示构造一个空function
function<T> f(obj);在f中存储可调用对象obj的副本
f将f作为条件:当f含有一个可调用对象是为真,否则为假
f(args)调用ff中的对象,参数是args
定义为function<T>的成员的类型
result_type该function类型的可调用对象返回的类型
argument_type当T有一个或两个实参时定义的类型。如果T只有一个实参,则argument_type是该类型的同义词,如果T有两个实参,则first_argument_type和second_argument_type分表代表 两个实参的类型
first_argument_type
second_argument_type

 

function是一个模板,当创建一个具体的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;   //打印6
cout << f2(4, 2) << endl;   //打印2
cout << f3(4, 2) << endl;   //打印8

 使用这个function类型我们可以重新定义map:

//列举可调用对象与二元运算符对应关系的表格
//所有可调用对象都必须接受两个int,返回一个int
//其中的元素可以是函数指针,函数对象或者lambda
map<string, function<int(int, int)>> binops;

我们把所有可调用对象,包括指针、lambda或者函数对象在内,都添加到这个map:

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);  //调用add(10, 5);
binops["-"](10, 5);  //使用std::minus<int>对象的调用运算符
binops["/"](10, 5);  //使用devide对象的调用运算符
binops["*"](10, 5);  //调用lambda函数对象
binops["%"](10, 5);  //调用lambda函数对象

重载的函数与function

我们不能(直接)将重载函数的名字存入function类型的对象中:

int add(int i, int j) { return i + j; }
Sales_data add(const Sales_data&, const Sales_data&);
map<string, function<int(int, int)>> binops;
binops.insert({"+", add}) ; //错误,哪个add?

解决办法1:

//存储函数指针而非函数的名字
int (*fp)(int, int) = add;  //指针所指向的add是接受两个int的版本
binops.insert({"+", fp}); // 正确,fp指向一个正确的add版本

解决办法2:

//使用lambda来指定我们希望使用的add版本
binops.insert({"+", [](int a, int b) { return add(a, b);}});

lambda内部的函数调用传入两个int,因此该调用只能匹配接受两个int的add版本,而这也正是执行lambda时真正调用的函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值