首页 > 编程语言 >C++可调用对象与function

C++可调用对象与function

时间:2024-10-21 21:11:31浏览次数:6  
标签:function 调用 int C++ 对象 add binops

目录

C++语言中可调用的对象

  • 函数
  • 函数指针
  • lambda表达式
  • bind创建的对象
  • 重载了函数调用运算符的类

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

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

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)

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

在C++语言中,函数表很容易通过map来实现。

//构建从运算符到函数指针的映射关系,其中函数接受两个int、返回一个int
map<string, int(*)(int, int)> binops;

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

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

但是我们不能将mod或者divide存入binops

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

问题在于mod是个lambda表达式,而每个lambda有它自己的类类型,该类型与存储在binops中的值的类型不匹配。

标准库function类型

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

function是一个模板,和其他模板一样,当创建一个具体的function类型时我们必须提供额为的信息。在此例中,所谓额外的信息是指该function类型能够表示的对象的调用形式。参考其他模板,我们在一对尖括号内指定类型:

function<int(int, int)>

在这里我们声明了一个function类型,它可以表示接受两个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对象
}

我们的map中包含5个元素,尽管其中的可调用对象的类型各不相同,我们仍然能够把所有这些类型都存储在同一个function<int(int, int)>类型中。

当我们索引map时将得到关联值的一个引用。如果我们索引binops,将得到function对象的引用。function类型重载了调用运算符,该运算符接受它自己的实参然后将其传递给存好的可调用对象。

binops["+"](10, 5);		//调用add(10, 5)
binops["-"](10, 5);		//调用minus<int>对象的调用运算符
binops["/"](10, 5);		//调用divide对象的调用运算符
binops["*"](10, 5);		//调用lambda函数对象
binops["%"](10, 5);		//调用lambda函数对象

我们依次调用了binops中存储的每个操作。在第一个调用中,我们获得的元素存放着一个指向add函数的指针,因此调用binops["+"](10, 5)实际上是使用该指针调用add,并10和5。在接下来的调用中,binops["-"]返回一个存放着std::minus<int>类型对象的function,我们将执行该对象的调用运算符。

重载的函数与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?

解决上述二义性问题的一条途径是存储函数指针而非函数的名字:

int (*fp)(int, int) = add;	//指针所指的add是接受两个int的版本
binops.insert({"+", fp});	//正确:fp指向一个正确的add版本

同样,我们也能使用lambda来消除二义性:

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

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

标签:function,调用,int,C++,对象,add,binops
From: https://www.cnblogs.com/milkchocolateicecream/p/18490424

相关文章

  • C/C++指针的概念
              指针作为C/C++中一个重要的概念,是每个C/C++程序员必备技能,今天就来说说它。一、指针的概念指针是一种变量,它存储的是另外一个变量的内存地址。在C/C++中,通过指针可以间接访问和操作内存中的数据。例如:intnum=0;int*ptr=&num;这里ptr是一个......
  • 数组的概念(C++)
        今天介绍一下数组。在C++中,数组就是一种用于存储相同类型元素的容器,也是一种数据结构,在编程中被广泛使用。一、定义与组成    数组是由相同类型的元素组成的集合,这些元素在内存中是连续存储的。例如,一个整数数组可以存储多个整数,一个字符数组可以存储......
  • C++程序设计基础实验1
    任务一:源代码task1.cpp#include<iostream>#include<string>#include<vector>#include<algorithm>usingnamespacestd;//声明//模板函数声明template<typenameT>voidoutput(constT&c);//普通函数声明voidtest1();voidtest2();void......
  • C++研发笔记4——C语言程序设计初阶学习笔记2
            从今天开始我们开始第二模块初识C语言的学习,在本模块中我们将会涉及到一下14个内容:什么是C语言、第一个C语言程序、数据类型、变量、常量、字符串+转义字符+注释、选择语句、循环语句、函数、数组、操作符、常见关键字、define定义常量和宏、指针......
  • CANoe_C#如何调用CANoe的诊断
    官网说明书位置1.引言CANoe的简介CANoe是由VectorInformatikGmbH开发的一款强大工具,广泛应用于汽车网络的开发和测试。它支持多种协议,包括CAN、LIN、FlexRay和Ethernet(如SOME/IP和DoIP)。CANoe不仅提供了图形用户界面(GUI)来配置和监控网络,还支持通过脚本和自动化API进行扩......
  • 异步函数 async function
    ◼async关键字用于声明一个异步函数:async是asynchronous单词的缩写,异步、非同步;sync是synchronous单词的缩写,同步、同时;◼async异步函数可以有很多中写法asyncfunctionfoo(){}constfoo1=asyncfunction(){}constfoo2=async()=>{}classPerson{asyncfoo......
  • C++静态成员变量和静态成员函数的使用
    1.静态成员变量:定义在类中的静态成员,以static关键字初始化和成员变量区别:a.可以通过类名::变量名进行调用,可访问性还是由(public,private,protected)进行限制 例如下面的mystaitcClass::_id,protected属性内容无法直接进行访问,若要直接访问需要修改为public2.静态成员函数:类似可以通过......
  • [Javascript] Write memoize function
    classMemoizeMap{constructor(){this._map=newMap();this._weakMap=newWeakMap();}_isObject(v){returntypeofv==="object"&&v!==null;}set(key,value){if(this._isObject(key)){this._......
  • 如何使用Python调用API数据
    为什么使用Python调用API数据?简洁的语法:Python的简洁性使得编写API调用代码变得直观易懂。强大的库支持:Python拥有如requests这样的库,极大地简化了HTTP请求的发送和响应的处理。数据处理能力:Python的数据处理库,如Pandas,使得数据的分析和处理变得简单。社区支持:Python社区庞大,......
  • 高效并行计算:使用C++中的std::thread实现多线程编程
    解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界在现代计算中,随着多核处理器的普及,如何充分利用硬件资源以提升程序性能成为关键问题之一。C++标准库提供了丰富的多线程支持,其中std::thread是用于实现并发计算的核心工具之一。通过合理的多线程设计,程序可以实现......