C++可变模板参数与包装器(function、bind)

news2024/11/24 4:59:18

文章目录

  • 一、 可变参数模板
    • 1. 概念
    • 2. 参数包值的获取
  • 二、 包装器
    • 1. 什么是包装器
    • 2. 为什么要有包装器
    • 3. std::function
      • (1) function概念
      • (2) 利用function解决实例化多份问题
      • (3) 包装器的其他使用场景:
    • 4. std::bind
      • (1) bind概念
      • (2) bind普通函数
      • (3) bind成员函数


一、 可变参数模板

1. 概念

C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。现阶段呢,我们掌握一些基础的可变参数模板特性就够我们用了,所以这里我们点到为止,以后大家如果有需要,再可以深入学习。
下面就是一个基本可变参数的函数模板

// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。

2. 参数包值的获取

  • 递归函数方式展开参数包
// 递归终止函数
void _ShowList()
{
	// 结束条件的函数
	cout << endl;
}

template <class T, class ...Args>
void _ShowList(T val, Args... args)
{
	cout << val << " ";
	_ShowList(args...);
}

//args代表0-N的参数包
template <class ...Args>
void CppPrint(Args... args)
{
	_ShowList(args...);
}

int main()
{
	CppPrint();
	CppPrint(1);
	CppPrint(1, 2);
	CppPrint(1, 2, 2.2);
	CppPrint(1, 2, 2.2, string("xxxx"));

	// ...

	return 0;
}

此处我们简要分析一下递归展开的原理:注意我们这里对函数 _ShowList进行了重载,可以根据有参还是无参自动调用匹配的函数。
以调用CppPrint()、和CppPrint(1, 2)为例。

  1. 当调用CppPrint()之后,会调用 _ShowList(args…)因为这里没有参数,所以这里调用的就是无参数的 _ShowList函数,输出一个换行就结束了。
  2. 当调用CppPrint(1, 2)之后,会调用_ShowList(args…),因为此时里面有两个参数,所以会调用有参的_ShowList(T val,Args… args)。这个时候第一个参数传给了val,并由模板推出来了类型,剩下的一个参数传给了可变参数包args。之后输出val,就相当于输出了1。之后再次调用_ShowList(args…),发现里面有一个参数,所以调用的还是有参的_ShowList(args…),之后继续输出2之后,继续调用_ShowList(args…)发现里面没参数了,就会调用无参的_ShowList()输出一个换行后结束。
    在这里插入图片描述
  • 逗号表达式展开参数包

这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式。expand函数中的逗号表达式:(printarg(args), 0),也是按照这个执行顺序,先执行printarg(args),再得到逗号表达式的结果0。同时还用到了C++11的另外一个特性——初始化列表,通过初始化列表来初始化一个变长数组, {(printarg(args), 0)…}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc… ),最终会创建一个元素值都为0的数组int arr[sizeof…(Args)]。由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包

template <class T>
void PrintArg(T t)
{
	cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{
	int arr[] = { (PrintArg(args), 0)... };
	cout << endl;
}
int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

在这里插入图片描述

二、 包装器

1. 什么是包装器

在C++中,包装器(Wrapper)通常用于封装和扩展现有类型或函数的功能。对于std::functionstd::bind,它们是C++标准库提供的两种非常有用的包装器,用于处理函数和可调用对象。

2. 为什么要有包装器

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。那么我们来看看,我们为什么需要function呢?

ret = func(x);

上面func可能是什么呢?那么func可能是函数名?函数指针?函数对象(仿函数对象)?也有可能是lambda表达式对象?所以这些都是可调用的类型!如此丰富的类型,可能会导致模板的效率低下!
为什么呢?我们继续往下看

在下面的代码中我们利用模板写了一个函数,在其中定义了一个static变量count,每次输出count的值并++,再输出count的地址,我们可以通过观察count的地址是否相同来观察通过传入类型的不同,模板实例化出一份还是多份。
补充知识:

在C++中,模板实例化会为每个不同的模板参数类型生成一个独立的类(或函数)的实例。这意味着如果你有一个函数模板,并且你用不同的类型参数来调用它,编译器会为每个不同的类型生成一个独立的函数实例。

关于函数模板中的静态变量,每个模板实例都会拥有它自己的静态变量副本。这些静态变量的地址和值是独立的,它们不会共享相同的内存位置。换句话说,对于每种不同的类型参数,函数模板都会实例化一个独立的静态变量。

template<class F, class T>
T useF(F f, T x)
{
	static int count = 0;
	cout << "count:" << ++count << endl;
	cout << "count:" << &count << endl;
	return f(x);
}
double f(double i)
{
	return i / 2;
}
struct Functor
{
	double operator()(double d)
	{
		return d / 3;
	}
};
int main()
{
	// 函数名
	cout << useF(f, 11.11) << endl;
	// 函数对象
	cout << useF(Functor(), 11.11) << endl;
	// lamber表达式
	cout << useF([](double d)->double { return d / 4; }, 11.11) << endl;
	return 0;
}

在这里插入图片描述

通过上面的程序验证,我们会发现每次count的地址是不一样的,并且count的值都是1,这说明useF函数模板实例化了三份。如果这个模板函数中的代码过多,每次都实例化一个新的出来的话效率就很低了,那有什么方法可以解决这个问题呢?答案是包装器,包装器可以很好的解决上面的问题

3. std::function

(1) function概念

std::function是一个通用的、多态的函数包装器。它可以存储、复制和调用任何Callable目标——函数、Lambda表达式、bind表达式或其他函数对象,只要这些目标具有与std::function相同的调用签名。

概念

std::function提供了一个类型擦除的机制,使得你可以存储不同类型的可调用对象,并在需要时调用它们,而无需关心它们的具体类型。

作用

  • 灵活性:你可以将任何满足调用签名的可调用对象赋值给std::function对象,这使得代码更加灵活和可重用。
  • 回调机制std::function经常用于实现回调函数机制,允许在运行时动态地指定要调用的函数或Lambda表达式。
  • 函数对象:可以作为函数对象使用,可以在STL算法等场合中作为参数传递。

function是一种函数包装器,也叫做适配器。它可以对可调用对象进行包装,C++中的function本质就是一个类模板,如下:

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;

模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

简单来说无论是函数指针、仿函数还是lambda都可以赋值给function,这样就把它们整合起来了。

// 使用方法如下:
#include <functional>
int f(int a, int b)
{
	return a + b;
}
struct Functor
{
public:
	int operator() (int a, int b)
	{
		return a + b;
	}
};
class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}
	double plusd(double a, double b)
	{
		return a + b;
	}
};
int main()
{
	// 函数名(函数指针)
	std::function<int(int, int)> func1 = f;
	cout << func1(1, 2) << endl;
	// 函数对象
	std::function<int(int, int)> func2 = Functor();
	cout << func2(1, 2) << endl;
	// lambda表达式
	std::function<int(int, int)> func3 = [](const int a, const int b)
	{return a + b; };
	cout << func3(1, 2) << endl;

	// 类的成员函数
	std::function<int(int, int)> func4 = &Plus::plusi;
	cout << func4(1, 2) << endl;
	std::function<double(Plus, double, double)> func5 = &Plus::plusd;
	cout << func5(Plus(), 1.1, 2.2) << endl;
	return 0;
}
  • 包装时指明返回值类型和各形参类型,然后将可调用对象赋值给function包装器即可,包装后function对象就可以像普通函数一样使用了。
  • 取静态成员函数的地址可以不用取地址运算符“&”,但取非静态成员函数的地址必须使用取地址运算符“&”。
  • 包装非静态的成员函数时需要注意,非静态成员函数的第一个参数是隐藏this指针,因此在包装时需要指明第一个形参的类型为类的类型。

(2) 利用function解决实例化多份问题

有了包装器,如何解决模板的效率低下,实例化多份的问题呢?

对于函数模板useF来说传入该函数模板的第一个参数可以是任意的可调用对象,比如函数指针、仿函数、lambda表达式等,但是由于它们的类型不同所以就会导致模板会被多次实例化,导致效率低下。

那我们如何解决这个问题呢?当然是直接把类型给他统一成一种这样不就只会实例化出一份了。所以我们可以使用function将函数指针、仿函数、lambda表达式等进行统一。
如下:

template<class F, class T>
T useF(F f, T x)
{
	static int count = 0;
	cout << "count:" << ++count << endl;
	cout << "count:" << &count << endl;
	return f(x);
}
double f(double i)
{
	return i / 2;
}
struct Functor
{
	double operator()(double d)
	{
		return d / 3;
	}
};
int main()
{
	// 函数名
	std::function<double(double)> func1 = f;
	cout << useF(func1, 11.11) << endl;
	// 函数对象
	std::function<double(double)> func2 = Functor();
	cout << useF(func2, 11.11) << endl;
	// lamber表达式
	std::function<double(double)> func3 = [](double d)->double { return d /4; };
	cout << useF(func3, 11.11) << endl;
	return 0;
}

这次可以看到count是3,而且每次打印出的count的地址都是相同的,这就说明只实例化出来一份,我们的目的达成了。
在这里插入图片描述

(3) 包装器的其他使用场景:

逆波兰表达式求解
求解方法:

  1. 定义一个栈,依次遍历所给字符串。
  2. 如果遍历到的字符串是数字则直接入栈。
  3. 如果遍历到的字符串是加减乘除运算符,则从栈定抛出两个数字进行对应的运算,并将运算后得到的结果压入栈中。
  4. 所给字符串遍历完毕后,栈顶的数字就是逆波兰表达式的计算结果。

原始代码:

class Solution {
public:
	int evalRPN(vector<string>& tokens) {
		stack<int> st;
		for (auto& str : tokens)
		{
			if (str == "+" || str == "-" || str == "*" || str == "/")
			{
				int right = st.top();
				st.pop();
				int left = st.top();
				st.pop();
				switch (str[0])
				{
				case '+':
					st.push(left + right);
					break;
				case '-':
					st.push(left - right);
					break;
				case '*':
					st.push(left * right);
					break;
				case '/':
					st.push(left / right);
					break;
				}
			}
			else
			{
				// 1、atoi itoa
				// 2、sprintf scanf
				// 3、stoi to_string C++11
				st.push(stoi(str));
			}
		}
		return st.top();
	}
};

使用function优化后的代码:

// 使用包装器以后的玩法
class Solution {
public:
	int evalRPN(vector<string>& tokens) {
		stack<int> st;
		map<string, function<int(int, int)>> opFuncMap =
		{
		{ "+", [](int i, int j) {return i + j; } },
		{ "-", [](int i, int j) {return i - j; } },
		{ "*", [](int i, int j) {return i * j; } },
		{ "/", [](int i, int j) {return i / j; } }
		};
		for (auto& str : tokens)
		{
			if (opFuncMap.find(str) != opFuncMap.end())
			{
				int right = st.top();
				st.pop();
				int left = st.top();
				st.pop();
				st.push(opFuncMap[str](left, right));
			}
			else
			{
				// 1、atoi itoa
				// 2、sprintf scanf
				// 3、stoi to_string C++11
				st.push(stoi(str));
			}
		}
		return st.top();
	}
};

4. std::bind

(1) bind概念

std::bind是一个函数适配器,用于将可调用对象(如函数、函数对象或Lambda表达式)与参数绑定,生成一个新的可调用对象。

概念

std::bind通过创建一个新的函数对象来绑定一个可调用对象及其参数。这个新的函数对象在调用时会将绑定的参数传递给原始的可调用对象。

作用

  • 参数绑定:你可以使用std::bind来预先绑定一些参数,生成一个新的可调用对象。这在需要固定某些参数值或调整参数顺序时非常有用。
  • 成员函数绑定std::bind特别适用于绑定类的成员函数和对象实例,从而创建一个可以调用该成员函数的函数对象。
  • 回调函数与事件处理:在事件驱动编程中,std::bind常用于生成回调函数,将事件与特定的处理函数绑定起来。

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。

原型如下:

template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);

可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。

调用bind的一般形式

auto newCallable = bind(callable,arg_list);

其中:

  • newCallable本身是一个可调用对象,
  • arg_list是一个逗号分隔的参数列表,对应给定的callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中的参数。
  • arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推。

(2) bind普通函数

例如在下面的程序中我们利用bind来绑定一个Sub函数。bind的第一个参数传一个可调用对象,这里我们传了一个函数指针Sub,因为函数Sub有两个参数,所以这里有placeholders::_1和 placeholders::_2分别代表两个参数。bind后赋值给一个包装器,之后我们就可以像调用函数一样去利用rSub来调用Sub了。

int Sub(int a, int b)
{
	return a - b;
}
int main()
{
	function<int(int, int)> rSub = bind(Sub, placeholders::_1, placeholders::_2);
	cout << rSub(10, 5) << endl;
	return 0;
}

在这里插入图片描述
这里我们还可以交换一下Sub函数中参数的位置,比如这样把placeholders::_1和 placeholders::_2交换一下位置,就可以改变参数的位置。
在这里插入图片描述

在这里插入图片描述
利用bind还可以搞类似于缺省参数的效果,比如说我们这里设计一个Plus函数,有三个参数,在bind时,前两个参数依旧是placeholders::_1,和placeholders::_2,但在第三个参数上我们选择直接指定具体的值,这样在调用的时候只需要传入前两个参数就可以了,比如Plus(5.3)。

double Plus(int a, int b, double rate)
{
	return (a + b) * rate;
}
int main()
{
	function<double(int, int)> Plus1 = bind(Plus, placeholders::_1, placeholders::_2, 4.0);
	function<double(int, int)> Plus2 = bind(Plus, placeholders::_1, placeholders::_2, 4.2);
	function<double(int, int)> Plus3 = bind(Plus, placeholders::_1, placeholders::_2, 4.4);

	cout << Plus1(5, 3) << endl;
	cout << Plus2(5, 3) << endl;
	cout << Plus3(5, 3) << endl;
	return 0;
}

在这里插入图片描述那如果我在设计一个PPlus函数,这次我们把rate放中间,这次我们选择确实中间这个参数又当如何操作呢?如果在函数中单纯依靠缺省参数的话是不可能解决这个问题的。但是使用bind就可以解决,如下。我们还是将第二个参数直接设置成确定的数值,然后将第一个和第三个参数设置成placeholders::_1和placeholders::_2。值得注意的是这里虽然是第一个参数和第三个参数,但不是使用placeholders::_1和placeholders::_3而是placeholders::_1和placeholders::_2

double PPlus(int a, double rate, int b)
{
	return  rate * (a + b);
}
int main()
{
	function<double(int, int)> PPlus1 = bind(PPlus, placeholders::_1, 4.0, placeholders::_2);
	function<double(int, int)> PPlus2 = bind(PPlus, placeholders::_1, 4.2, placeholders::_2);
	cout << PPlus1(5, 3) << endl;
	cout << PPlus2(5, 3) << endl;
	return 0;
}

在这里插入图片描述

(3) bind成员函数

  • 如果绑定的成员函数是static的在bind传递第一个参数时指定类名::成员函数名就可以了,其他的和上面的一样
  • 但要是非静态成员函数的话因为非静态成员函数第一个参数默认时this指针嘛,所以我们在传递的时候要在第二个参数中传一个对象的地址进去比如下面代码中的==&st==,或者传入一个匿名对象也可以比如SubType()
class SubType
{
public:
	static int sub(int a, int b)
	{
		return a - b;
	}

	int ssub(int a, int b, int rate)
	{
		return (a - b) * rate;
	}
};

int main()
{
	function<double(int, int)> Sub1 = bind(&SubType::sub, placeholders::_1, placeholders::_2);

	SubType st;
	function<double(int, int)> Sub2 = bind(&SubType::ssub, &st, placeholders::_1, placeholders::_2, 3);
	cout << Sub1(1, 2) << endl;
	cout << Sub2(1, 2) << endl;

	function<double(int, int)> Sub3 = bind(&SubType::ssub, SubType(), placeholders::_1, placeholders::_2, 3);
	cout << Sub3(1, 2) << endl;

	cout << typeid(Sub3).name() << endl;

	return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1580957.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

水资源管理系统:守护生命之源,构建和谐水生态

水资源是维系地球生态平衡和人类社会可持续发展的重要基础。然而&#xff0c;随着人口增长、工业化和城市化的加速&#xff0c;水资源短缺、水质污染和生态破坏等问题日益凸显。在这样的背景下&#xff0c;构建一个全面、高效、智能的水资源管理系统显得尤为迫切和必要。 项目…

Google Cookie意见征求底部弹窗

关于欧盟 Cookie 通知 根据2024年欧盟的《通用数据保护条例》以及其他相关法规&#xff0c;要求google cookie的使用必须征求用户的同意&#xff0c;才能进行收集用户数据信息&#xff0c;因此跨境独立站&#xff0c;如果做欧洲市场&#xff0c;就必须弹出cookie收集数据弹窗&a…

阿赵UE学习笔记——26、动画混合空间

阿赵UE学习笔记目录   大家好&#xff0c;我是阿赵。   继续学习虚幻引擎的使用。之前学习了通过蓝图直接控制动画播放&#xff0c;或者通过动画状态机去控制播放。这次来学习一种比较细致的动画控制播放方式&#xff0c;叫做动画混合空间。 一、使用的情景 假设我们现在需…

链表的中间结点——每日一题

题目链接&#xff1a; OJ链接 题目&#xff1a; 给你单链表的头结点 head &#xff0c;请你找出并返回链表的中间结点。 如果有两个中间结点&#xff0c;则返回第二个中间结点。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[3,4,5] 解释&…

2023一个前端人的杂谈

酒香也怕巷子深 年底提车,回河北过年,一路总是旅游的牌子,后来去满城滑雪,随拍了几张照片,才更加感受河北的魅力。 感觉仅仅是这一抹黄昏,就让这一行物超所值了,原来那句宣传语所言非虚:这么近,那么美,周末到河北,然而我认为实际的好处,可能不止如此。 作为一个出…

ADC电路项目1——10bit SAR ADC 设计,smic18工艺,有工艺库,有效位数ENOB为9.8

分享一个入门SAR ADC的完整电路项目&#xff0c;适合新手小白学习 10bit 20MHz SAR ADC&#xff08;WX:didadidadidida313&#xff0c;加我备注&#xff1a;CSDN 10 bit SAR ADC&#xff0c;谢绝白嫖哈&#xff09; 概述&#xff1a; 本设计采用 smic18mmrf CMOS 工艺&#xf…

23linux 自定义shell文件系统

打印环境变量&#xff0c;把当前子进程所有环境变量打印出来 环境变量也是一张表&#xff08;指针数组以null结尾&#xff0c;最后条件不满足就退出了 &#xff09; 用子进程调用 结论1 当我们进行程序替换的时候 &#xff0c;子进程对应的环境变量&#xff08;子进程的环境变…

2024年3月30日~2024年4月7日周报

文章目录 一、前言二、创意收集2.1 多任务学习2.1.1 多任务学习的定义与优势2.1.2 多任务学习的分类 2.2 边缘检测2.2.1 基础理论2.2.2 sobel代码介绍2.2.3 canny代码介绍 三、《地震速度模型超分辨率的多任务学习》3.1 M-RUDSR架构3.2 详细介绍3.3 实验设置 四、实验五、小结5…

网工内推 | 深信服、宁德时代,最高20K招安全工程师,包吃包住

01 深信服科技 招聘岗位&#xff1a;安全服务工程师 职责描述&#xff1a; 1.负责现场安全服务项目工作内容&#xff0c;包含渗透测试、安全扫描、基线核查、应急响应等&#xff1b; 2.协助用户完成安全测试漏洞整改、复测工作&#xff1b; 3.为用户提供网络、主机、业务系统等…

数据库讲解---(数据查询)【MySQL版本】

零.前言 数据库讲解&#xff08;MySQL版&#xff09;&#xff08;超详细&#xff09;【第一章】-CSDN博客 数据库-ER图教程_e-r图数据库-CSDN博客 数据库讲解&#xff08;MySQL版&#xff09;&#xff08;超详细&#xff09;【第二章】【上】-CSDN博客 数据库讲解---(SQL语…

[从0开始AIGC][Transformer相关]:算法的时间和空间复杂度

一、算法的时间和空间复杂度 文章目录 一、算法的时间和空间复杂度1、时间复杂度2、空间复杂度 二、Transformer的时间复杂度分析1、 self-attention 的时间复杂度2、 多头注意力机制的时间复杂度 三、transformer的空间复杂度 算法是指用来操作数据、解决程序问题的一组方法。…

专题十二、字符串

字符串 1. 字符串字面量1.1 字符串字面量中的转义序列1.2 延续字符串字面量1.3 如何存储字符串字面量1.4 字符串字面量的操作1.5 字符串字面量与字符常量 2. 字符串变量2.1 初始化字符串变量2.2 字符数组与字符指针 3. 字符串的读和写3.1 用 printf 函数和 puts 函数写字符串3.…

通俗白话了解资产负债现金利润三张表

看到一本小书不错《财务小白轻松入门》&#xff0c;里面通俗说了三张表之间的关系。贴图摘录下&#xff1a;

SSL证书添加与ICP备案,对于SpringBoot的要求

配置了SSL证书之后&#xff0c;在SpringBoot的resources文件夹里的application.properties会添加以下代码&#xff1a; server.port443 不需要添加server.address。不然会报错。 https类型的请求默认在Postman里面不可请求。 经过SSL证书处理的网页&#xff0c;链接中使默认…

RAG文本加载和分块调研

文本加载和分块 一、文本加载 文本加载是RAG文本增强检索重要环节。文件有不同类型&#xff08;excel、word、ppt、pdf、png、html、eps、gif、mp4、zip等&#xff09;&#xff0c;衍生出了很多第三方库。使用python处理文件是各种python开发岗位都需要的操作。主要涉及到的标准…

TryHackMe - HTTP Request Smuggling

学完、打完后的复习 HTTP 1 这部分比较简单&#xff0c;直接略过 HTTP2请求走私 首先要了解HTTP2的结构&#xff0c;与HTTP1之间的一些差异 HTTP2中不再使用CRLF来作为字段的边界限定&#xff0c;而是在二进制中直接通过长度、名字、值长度、值&#xff0c;来确认边界 而这…

【C++】用红黑树封装map和set

我们之前学的map和set在stl源码中都是用红黑树封装实现的&#xff0c;当然&#xff0c;我们也可以模拟来实现一下。在实现之前&#xff0c;我们也可以看一下stl源码是如何实现的。我们上篇博客写的红黑树里面只是一个pair对象&#xff0c;这对于set来说显然是不合适的&#xff…

软件可靠性基本概念_1.定义和定量描述

1.软件可靠性定义 软件可靠性&#xff08;Software Reliability&#xff09;是软件产品在规定的条件下和规定的时间区间完成规定功能的能力。规定的条件是指直接与软件运行相关的使用该软件的计算机系统的状态和软件的输入条件&#xff0c;或统称为软件运行时的外部输入条件&am…

【MATLAB源码-第183期】基于matlab的图像处理GUI很全面包括滤波,灰度,边缘提取,RGB亮度调节,二值化等。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 1. RGB颜色亮度调整 1.1 RGB颜色模型 RGB颜色模型是一种加色模型&#xff0c;使用红色&#xff08;R&#xff09;、绿色&#xff08;G&#xff09;、蓝色&#xff08;B&#xff09;三种颜色的不同组合来表示各种颜色。每种…

OceanBase V4.2 MySQL模式下,如何通过DBLINK实现跨数据源访问

概述 跨数据源访问可通过 DBLINK&#xff08;以下简称DBLINK&#xff09;实现&#xff0c;从而使得业务代码能够像访问本地数据库一样轻松访问远端数据库。原先&#xff0c;DBLINK主要服务于Oracle模式&#xff0c;但由于OceanBase 的MySQL模式租户同样存在访问远端数据库的需…