C++标准库:可调用对象与 function

本文为《C++ Primer》的读书笔记

可调用对象与调用形式

  • C++ 中有几种可调用的对象: 函数、函数指针、lambda 表达式、bind 创建的对象以及重载了函数调用运算符的类
  • 和其他对象一样, 可调用的对象也有类型。例如, 每个 lambda 有它自己唯一的(未命名)类类型; 函数及函数指针的类型则由其返回值类型和实参类型决定, 等等

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

  • 然而, 两个不同类型的可调用对象却可能共享同一种 调用形式(callsignature)。调用形式指明了调用返回的类型以及传递给调用的实参类型。一种调用形式对应一·个函数类型, 例如:
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 denorninator / divisor;
	};
};
  • 上面这些可调用对象尽管它们的类型各不相同, 但是共享同一种调用形式:
int (int, int)
  • 我们可能希望使用这些可调用对象构建一个简单的桌面计算器。为了实现这一目的,需要定义一个函数表 (function table) 用于存储指向这些可调用对象的 “指针”。当程序需要执行某个特定的操作时, 从表中查找该调用的函数
  • 函数表很容易通过 map 来实现。对于此例来说, 我们使用一个表示运算符符号的 string 对象作为关键字;使用实现运算符的函数作为值
  • 假定我们的所有函数都相互独立, 并且只处理关于 int 的二元运算, 则 map 可以定义成如下的形式:
// 构建从运算符到函数指针的映射关系, 其中函数接受两个 int、返回一个 int
map<string, int(*) (int, int)> binops;

// 将 `add` 的指针添加到 `binops` 中
binops.insert({
    
    "+", add}); 
  • 但是我们不能将mod或者divide存入binops。问题在于mod是个 lambda 表达式, 而每个 lambda 有它自己的类类型, 该类型与存储在 binops 中的值的类型不匹配

标准库 function 模板类

#include <functional>

在这里插入图片描述


  • 下面的 function 类型表示接受两个int、返回一个int的可调用对象。甚至只要可调用对象的参数和返回类型能够隐式转化为 int 即可
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) // lambda
								{
    
     return i * j; };
cout << f1(4,2) << endl; 
cout << f2(4,2) << endl;
cout << f3(4,2) << endl;
  • 使用这个 function 类型我们可以重新定义 map:
map<string, function<int(int, int)>> binops = {
    
    
	{
    
    "+", add}, 				// 函数指针
	{
    
     "-", std::minus<int>()},	// 标准库函数对象
	{
    
    "I", divide()},			// 用户定义的函数对象
	{
    
    "*", [](int i, int j) {
    
     return i * j; }},	// 未命名的lambda
	{
    
    "%", mod}};				// 命名了的 lambda 对象
};
binops["+"](10, 5); //调用 add(10, 5)
binops["-"](10, 5); //使用 minus<int> 对象的调用运算符
binops["/"](10, 5); //使用 divide 对象的调用运算符
binops["*"](10, 5); //调用 lambda 函数对象
binops["%"](10, 5); //调用 lambda 函数对象

int a, b;
string op;
cin >> a >> op >> b;
cout << binops[op](a, b) << endl;

重载函数 与 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);} });

猜你喜欢

转载自blog.csdn.net/weixin_42437114/article/details/113805467