首页 > 编程语言 >【C++】C++11之函数对象,Lambda表达式和functional函数对象类型

【C++】C++11之函数对象,Lambda表达式和functional函数对象类型

时间:2024-11-07 21:46:24浏览次数:6  
标签:11 cout int C++ 捕捉 类型 lambda 函数

知识的学习在于点滴记录,坚持不懈

函数对象

        重载了函数调用运算符()的类的对象,即为函数对象。

        std::function 由上文可以看出:由于可调用对象的定义方式比较多,但是函数的调用方式较为类似,因此需要使用一个统一的方式保存可调用对象或者传递可调用对象。于是,std::function就诞生了。

        std::function是一个可调用对象包装器,是一个类模板,可以容纳除了类成员函数指针之外的所有可调用对象,它可以用统一的方式处理函数、函数对象、函数指针,并允许保存和延迟它们的执行。

        std::function的使用用法:

#include <iostream>
#include <string>
#include <functional>
#include <map>
using namespace std;
void hello1()
{
	cout << "hello world!" << endl;
}
void hello2(string str)
{
	cout << str << endl;
}
int sum(int a, int b)
{
	return a + b;
}
class Test
{
public: // 必须依赖一个对象
	void hello(string str)
	{
		cout << str << endl;
	}
};
int main()
{
	/*
	1.用函数类型实例化function
	2.通过function调用operator()函数的时候,需要根据函数类型传入相应的参数
	*/
	// 从function的类模范定义处,看到希望用一个函数类型实例化function
	function<void()> func1(hello1);
	func1(); // func1.operator() -> hello1()

	function<void(string)> func2(hello2);
	func2("hello2"); // func2.operator()(string str) -> hello2(str)

	function<int(int, int)> func3 = sum;
	cout << func3(20, 30) << endl;

	function<int(int, int)> func4 = [](int a, int b)->int {return a + b; };
	cout << func4(100, 200) << endl;

	function<void(Test*, string)> func5 = &Test::hello;
	func5(&Test(), "call Test::hello");
}

        1.用函数类型实例化function。
         2.通过function调用operator()函数的时候,需要根据函数类型传入相应的参数。

        我们知道了function类型怎么使用了之后,来了解了解它的原理是什么????我们可以自己来实现一个myfunction类型:

#include <iostream>
#include <functional>
#include <string>
using namespace std;
void hello(string str){ cout << str << endl;}
int sum(int a, int b)
{
	return a + b;
}
template<typename Fty>
class myfunction{};
template<typename R, typename A1>
class myfunction<R(A1)>
{
public:
	using PFUNC = R(*)(A1);
	myfunction(PFUNC pfunc) :_pfunc(pfunc){}
	R operator()(A1 arg)
	{
		return _pfunc(arg);
	}
private:
	PFUNC _pfunc;
};

        我们先提供了一个类模板,然后使用using定义了一个类型为PFUNC为函数的类型,给出构造函数,传入的参数就是函数的函数名,重载了()运算符,传入了参数,最后调用hello函数,实现了function类,但是这个myfunction类型只能实现这一个函数传入,我们不能每写一个函数就定义一个function类型,C++11给我们提供了这样的功能,模板的全部特例化,所以,我们只需要写一个类来代表所有的类型。

// function函数对象类型的实现原理
#include <iostream>
#include <functional>
#include <string>
using namespace std;
void hello(string str){ cout << str << endl;}
int sum(int a, int b)
{
	return a + b;
}
template<typename Fty>
class myfunction{};
template<typename R, typename... A>
class myfunction<R(A...)>
{
public:
	using PFUNC = R(*)(A...);
	myfunction(PFUNC pfunc) :_pfunc(pfunc) {}
	R operator()(A... arg)
	{
		return _pfunc(arg...);
	}
private:
	PFUNC _pfunc;
};
int main()
{
	myfunction<void(string)> func1(hello);
	func1("hello world!");
	//myfunction<int(int, int)> func2(sum);
	return 0;
}

什么是模板的完全特例化和非完全(部分)特例化??

template<typename T>
bool compare(T a, T b)
{
	return a > b;
}
int main()
{
	compare(23, 30);
	return 0;
}

        根据这段代码,我们根据模板的实参推演,推演出形参的类型T是一个int类型,然后它会从模板函数中实例化出来一个关于int类型的函数出来进行编译。

        而我们要比较两个字符的大小,我们需要特例化一个函数:

template<typename T>
bool compare(T a, T b)
{
	cout << "T" << endl;
	return a > b;
}
template<>
bool compare<const char*>(const char* a, const char* b)
{
	cout << "const char*" << endl;
	return strcmp(a, b);
}
int main()
{
	compare(23, 30);
	compare("aaa", "bbb");
	return 0;
}

        它的运行结果为:

        这个就叫做完全特例化,因为这个T是完全已知的,就是const char* 类型的。也就是说明,这个模板只针对const char *类型特例化出来了一个模板实例化。

        有可能,我们对指针类型的类的构造会有改变,我们不知道它是什么指针,只知道它是一个指针类型的,我们则可以这特例化,对指针的类型进行部分特例化。

        下面这个是对指针类型提供的部分特例化版本:

template<typename Ty>
class vector<Ty*>
{
public:
	vector() { cout << "call vector:: <Ty*> init" << endl; }
private:
};
int main()
{
	vector<int> vec1;
	vector<char*> vec2;
	vector<int*> vec3;
}

        所以,有完全特例化的它就匹配完全特例化进行实例化,没有就寻找部分特例化进行实例化,都没有那么它就使用原模版进行实例化。

Lambda表达式

        Lambda有很多叫法,有Lambda表达式、Lambda函数、匿名函数,本文中为了方便表述统一用Lambda表达式进行叙述。 ISO C++标准官网展示了一个简单的lambda表示式实例:

#include <algorithm>
#include <cmath>

void abssort(float* x, unsigned n) {
    std::sort(x, x + n,
        // Lambda expression begins
        [](float a, float b) {
            return (std::abs(a) < std::abs(b));
        } // end of lambda expression
    );
}

         在上面的实例中std::sort函数第三个参数应该是传递一个排序规则的函数,但是这个实例中直接将排序函数的实现写在应该传递函数的位置,省去了定义排序函数的过程,对于这种不需要复用,且短小的函数,直接传递函数体可以增加代码的可读性。

lambda表达式语法

 

[capture-list] : 捕捉列表 ,该列表总是出现在 lambda 函数的开始位置, 编译器根据 [] 判断接下来的代码是否为 lambda 函数捕捉列表能够捕捉上下文中的变量供 lambda 函数使用 。 (parameters) :参数列表。与 普通函数的参数列表一致 ,如果不需要参数传递,则可以 连同 () 一起省略 mutable :默认情况下, lambda 函数总是一个 const 函数, mutable 可以取消其常量 性。使用该修饰符时,参数列表不可省略 ( 即使参数为空 ) 。 ->returntype :返回值类型 。用 追踪返回类型形式声明函数的返回值类型 ,没有返回 值时此部分可省略。 返回值类型明确情况下,也可省略,由编译器对返回类型进行推 {statement} :函数体 。在该函数体内,除了可以使用其参数外,还可以使用所有捕获 到的变量。 注意:         在lambda 函数定义中, 参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为 。因此 C++11 中 最简单的 lambda 函数为: []{} ; 该 lambda 函数不能做任何事情。 捕获列表说明         捕捉列表描述了上下文中那些数据可以被lambda 使用 ,以及 使用的方式传值还是传引用 。 [var] :表示值传递方式捕捉变量 var [=] :表示值传递方式捕获所有父作用域中的变量 ( 包括 this) [&var] :表示引用传递捕捉变量 var [&] :表示引用传递捕捉所有父作用域中的变量 ( 包括 this) [this] :表示值传递方式捕捉当前的 this 指针 注意:         a. 父作用域指包含 lambda 函数的语句块         b. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割 。 比如:[=, &a, &b] :以引用传递的方式捕捉变量 a 和 b ,值传递方式捕捉其他所有变量 。 [&,a, this] :值传递方式捕捉变量 a 和 this ,引用方式捕捉其他变量         c. 捕捉列表不允许变量重复传递,否则就会导致编译错误 。 比如:[=, a] : = 已经以值传递方式捕捉了所有变量,捕捉 a 重复。         d. 在块作用域以外的 lambda 函数捕捉列表必须为空 。         e. 在块作用域中的 lambda 函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者 非局部变量都会导致编译报错。         f. lambda 表达式之间不能相互赋值 ,即使看起来类型相同。
template<typename T>
class TestLambda
{
public:
	TestLambda() {}
	void operator()()
	{
		cout << "hello world!" << endl;
	}
};
int main()
{
	auto a = []() -> void {cout << "hello world!" << endl; };
	a();
	TestLambda<int> t1;
	t1();
	return 0;
}

        看上面的代码,是一个函数对象,重载()运算符。Lambda表达式中,[]中的内容就相当于TestLambda类中构造函数的参数列表,()就相当于类中()重载函数的传入的参数,返回类型就是()重载函数的返回类型,函数体就是()重载函数里面的函数体。

        如果Lambda表达式的返回值不需要,指向符和返回值是可以省略的。

[]:表示不捕获任何外部变量。

[=]:以传值的方式捕获外部的所有变量。

[&]:以传引用的方式捕获外部的所有变量。

[this]:捕获外部的this指针。

[=,&a]:以传值的方式捕获外部的所有变量,但是a变量以传引用的方式捕获。

[a,b]:以传值的方式捕获外部变量a和b。

[a, &b]:a以值传递捕获,b以传引用的方式捕获。

template<typename T>
class TestLambda
{
public:
	TestLambda() {}
	void operator()()
	{
		cout << "hello world!" << endl;
	}
};
int main()
{
	auto func = []() -> void {cout << "hello world!" << endl; };
	func();
	TestLambda<int> t1;
	t1();
	auto func1 = [](int a, int b) -> int {return a + b; };
	cout <<func1(10, 20) << endl;
	int a = 10;
	int b = 20;
	auto func3 = [&a, &b]()mutable->void 
	{
		int temp = a;
		a = b;
		b = temp; 
	};
	func3();
	cout << a << b << endl;
	return 0;
}

        operator()在底层默认是一个常方法,即就是在函数体内不能修改成员变量的值,所以我们在交换值的时候,需要在()后加上一个mutable,表示可以修改值,或者是我们不使用按值传递,我们使用按引用传递,传入a,b的引用,这样就能交换a,b的值啦!

Lambda表达式的使用

int main()
{
	vector<int> vec;
	for (int i = 0; i < 20; i++)
	{
		vec.push_back(rand() % 100 + 1);
	}
	sort(vec.begin(), vec.end(), [](int a, int b)->bool {return a > b; });
	for (int v : vec)
	{
		cout << v << "  ";
	}
	cout << endl;
	// 65按顺序插入序列,找第一个小于65的数字的位置
	auto it = find_if(vec.begin(), vec.end(), [](int a)->bool { return a < 65; });
	if (it != vec.end())
	{
		vec.insert(it, 65);
	}
	for (int v : vec)
	{
		cout << v << "  ";
	}
	cout << endl;
	for_each(vec.begin(), vec.end(), [](int a){
		if(a%2==0)
			cout << a << "  ";
	});
	return 0;
}

Lambda表达式只能使用在语句当中,如果想跨语句使用之前定义好的lambda表达式,应该怎么办??用什么类型来表示lambda表达式???

lambda表达式是一个函数对象,用function来进行表示。

标签:11,cout,int,C++,捕捉,类型,lambda,函数
From: https://blog.csdn.net/m0_73839343/article/details/143605814

相关文章

  • 2024.11.7随笔
    前言觉得就两三个人在机房安静自习真的好,有很多事情要做,规划好后按计划走不会感到迷茫而无所适从,头脑中也有时间的意识。只能说我个人比较喜欢对时间的掌控感,也喜欢安静的环境。明天大家就都要归队了,不知道下一次这么安静又要等到多久?写题今天水了个三倍经验所以就过了六道题,然......
  • YOLOv11 正式发布!你需要知道什么? 另附:YOLOv8 与YOLOv11 各模型性能比较
    YOLOv11目标检测创新改进与实战案例专栏点击查看文章目录:YOLOv11创新改进系列及项目实战目录包含卷积,主干注意力,检测头等创新机制以及各种目标检测分割项目实战案例点击查看专栏链接:YOLOv11目标检测创新改进与实战案例2024年9月30日,Ultralytics在他们的YOLOVision活动......
  • C++循环引用指的是什么,在使用过程当中需要注意什么问题
    C++中的循环引用是指两个或多个对象相互持有对方的引用,导致这些对象无法被自动释放,从而造成内存泄漏。循环引用主要发生在使用智能指针(如 std::shared_ptr)管理对象生命周期时。以下是循环引用的具体解释及其使用中需要注意的问题:循环引用的形成当两个对象A和B互相持......
  • cpp_9_8.9【(修改函数)计算一个数的p次幂】
    #include<stdio.h>doublepower(doublen,intp);//ANSI函数原型intmain(void){doublex,xpow;intexp;printf("Enteranumberandthepositiveintegerpower");printf("towhich\nthenumberwillberaised.Enterq......
  • cpp_9【用指针在更改主函数中的变量值】
    5.编写并测试一个函数larger_of(),该函数把两个double类型变量的值替换为较大的值。例如,larger_of(x,y)会把x和y中较大的值重新赋给两个变量。#include<stdio.h>voidlarger_of(double*x,double*y){ if(*x>*y){ *y=*x; } elseif(*y>*x){......
  • 11月7日 NOIP模拟(难题math、矩阵游戏matrix、括号序列seq、道路road) - 模拟赛记录
    PrefaceT1试图找规律失败,正经推反而几分钟就出来了。以后应该少想这些歪门邪道(除非实在闲的蛋疼或者没有一点头绪,且必须要打完所有能打的子任务比如暴力或特殊性质;而且必须在用常规方法思考过后,才能够用一些稍微不那么常规的方法)至于T2、T3、T4,因为知道T1浪费了太多时间,都是......
  • 2024/11/5日工作总结
    学习JS基础知识:1.引入方式:点击查看代码<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title></head><body><!--内部脚本--><!--<script>alert(......
  • 11.7 HTML
    Html一、基本介绍1、定义:html是一种超文本标记语言,也是一种标识性语言(不是编程语言)标记:记号(绰号)超文本:就是页面内容可以包含图片、链接,音乐,视频等素材。2、为什么学习html?(1)测试页面元素,了解页面页面元素(页面是html语言编写的)(2)进行ui自动化需用到元素定位3、html有哪些特点......
  • 2024年11月随便做做
    十月太摆了没有随便做做环节。测试题目选集20241106-D.盼君勿忘题解等会写qwq。Miscellaneous[AGC022D]Shopping神秘题目,比较酷。首先发现对于\(t_i\ge2L\)的\(t_i\)可以直接将\(t_i\lfloor\frac{t_i}{2L}\rfloor\)加入答案并将\(t_i\)对\(2L\)取模。然后只......
  • 11.7日
    创建一个新的DynamicWebProject打开Eclipse。选择File->New->DynamicWebProject。在弹出的对话框中,输入项目名称,例如MyWebApp。确保Targetruntime设置为ApacheTomcatv9.0(或其他你安装的Tomcat版本)。如果没有配置,点击NewRuntime按钮进行配置。点击Finish......