【C++11】常用新语法②(类的新功能 || 可变参数模板 || lambda表达式 || 包装器)

news2025/1/11 14:55:49

在这里插入图片描述

🔥个人主页: Forcible Bug Maker
🔥专栏: C++

目录

  • 🌈前言
  • 🔥类的新功能
    • 新增默认成员函数
    • 强制生成默认函数的关键字default
    • 禁止生成默认函数的关键字delete
  • 🔥可变参数模板
    • 递归函数方式展开参数包
    • 逗号表达式展开参数包
    • STL容器中的emplace相关接口函数
  • 🔥lambda表达式
    • lambda表达式
    • lambda表达式语法
    • 函数对象和lambda表达式
  • 🔥包装器
    • function
    • bind
  • 🌈结语

🌈前言

本篇博客主要内容:C++11中较为常用的新语法。

咱们继续接着上篇C++11来讲,本篇博客会涉及到C++11中新的两个默认成员函数,lambda表达式以及包装器相关的内容。

🔥类的新功能

新增默认成员函数

在原来所介绍的C++类中,有六个默认成员函数:

  1. 构造函数
  2. 析构函数
  3. 拷贝构造函数
  4. 拷贝赋值重载
  5. 取地址重载
  6. const取地址重载

默认成员函数就是我们不写编译器会生成一个默认的。C++11中,又新增了两个,它们基于右值引用语法而出现,分别是:移动构造函数移动赋值重载
对于这两个函数,需要注意以下几点:

  • 当你自己没有实现移动构造函数,析构函数,拷贝构造,拷贝复制重载中的任意一个时,那么编译器会自动生成一个默认移动构造。
    默认生成的移动构造函数,对于内置类型的成员会按字节拷贝,对于自定义类型的成员,则需要看其是否实现了移动构造,如果实现了就调用移动构造,否则就调用其拷贝构造。
  • 当你自己没有实现移动复制重载,析构函数,拷贝构造,拷贝赋值重载中的任意一个时,那么编译器会自动生成一个移动赋值重载。
    默认生成的移动构造函数,对于内置类型的成员会按字节拷贝,对于自定义类型的成员,则需要看其是否实现了移动赋值重载,如果实现了就调用移动赋值重载,否则就调用其拷贝赋值。(移动赋值和上面的移动构造完全类似)
  • 如果你提供了移构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

强制生成默认函数的关键字default

C++11为了更好的控制要使用的默认函数,可以强制使一些因规则不必生成的默认函数生成。如:我们提供了拷贝构造,就不会生成移动构造了,那么我们这时就可以用default关键字显示指定移动构造生成。

class Date
{
public:
	Date(int year = 2000, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

	Date(const Date& d)
		:_year(d._year)
		,_month(d._month)
		,_day(d._day)
	{}

	// 强制生成
	Date(Date&& d) = default;

private:
	int _year;
	int _month;
	int _day;
};

禁止生成默认函数的关键字delete

如果想限制某些默认函数的生成,C++98提供的解决方案是,将该函数设置为private,并且只声明不实现,这样只要其他人想调用就会报错。在C++11中更简单,只需要在该函数声明后加上= delete即可,该语法指示编译器不生成对应函数的默认版本,称delete修饰的函数为删除函数。

class Date
{
public:
	Date(int year = 2000, int month = 1, int day = 1)
		:_year(year)
		,_month(month)
		,_day(day)
	{}

	// 禁止拷贝构造
	Date(const Date& d) = delete;
	
private:
	int _year;
	int _month;
	int _day;
};

🔥可变参数模板

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

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

注:模板参数包名Args和函数形参参数包名args都是像变量一样可变的。

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

递归函数方式展开参数包

// 递归终止函数
template <class T>
void ShowList(const T& t)
{
	cout << t << endl;
}
// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{
	cout << value << " ";
	ShowList(args...);
}
int main()
{
	ShowList(1);
	ShowList(1, 'A');
	ShowList(1, 'A', std::string("sort"));
	return 0;
}

在这里插入图片描述
通过将参数包逐层传递解析,最终可以遍历到参数包中的值。

逗号表达式展开参数包

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;
}

这种展开方式,不需要通过递归终止函数,直接在函数体中展开,同时运用了C++11的另一个特性——初始化列表,通过初始化一个变长数组,最终会创建一个元素值都为0的数值int arr[sizeof(Args)]。expand函数中的逗号表达式:(printarg(args), 0),先执行printarg(args),再返回逗号表达式的结果0。由于是逗号表达式,在创建数组的过程中就将参数包展开了,这个数组的目的纯粹是我为了在构造的过程中展开参数包。
在这里插入图片描述

STL容器中的emplace相关接口函数

在这里插入图片描述
在这里插入图片描述
vector::emplace_back
list::emplace_back

template <class... Args>
void emplace_back (Args&&... args);

emplace系列的接口,支持模板的可变参数,并且是万能引用。那么emplace系列相对于传统的push_back和insert的优势体现在哪里呢?

std::list< std::pair<int, char> > mylist;
// emplace_back支持可变参数,拿到构建pair对象的参数后自己去创建对象
// 那么在这里我们可以看到除了用法上,和push_back没什么太大的区别
mylist.emplace_back(10, 'a');
mylist.emplace_back(20, 'b');
mylist.emplace_back(make_pair(30, 'c'));

mylist.push_back(make_pair(40, 'd'));
mylist.push_back({ 50, 'e' });

for (auto e : mylist)
cout << e.first << ":" << e.second << endl;

其底层的区别是,emplace_back对于插入对象是直接构造;而push_back是先构造,再移动构造。相比之下emplace少了移动构造,效率会高些。

🔥lambda表达式

C++98中提供了sort算法,用于排序,每当排序结构体这种类型的元素时,就需要我们重新去写一个类。如果每次比较逻辑不同,还需要实现多个类,特别是相同类的命名,这些都给编程者带来了极大的不便。因此,在C++11语法中出现了Lambda表达式。

lambda表达式

struct Goods
{
	string _name; // 名字
	double _price; // 价格
	int _evaluate; // 评价
	Goods(const char* str, double price, int evaluate)
		:_name(str)
		, _price(price)
		, _evaluate(evaluate)
	{}
};

int main()
{
	vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2,
	3 }, { "菠萝", 1.5, 4 } };
	// 价格升序
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return g1._price < g2._price; });
	//价格降序
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return g1._price > g2._price; });
	// 评估升序
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return g1._evaluate < g2._evaluate; });
	// 评估降序
	sort(v.begin(), v.end(), [](const Goods& g1, const Goods& g2) {
		return g1._evaluate > g2._evaluate; });
	return 0;
}

上述代码就是使用C++的lambda表达式来解决,可以看出lambda表达式实际是一个匿名函数。

lambda表达式语法

lambda表达式书写格式:

[capture-list] (parameters) mutable ->return-type { statement}

表达式各部分说明:

  • [capture-list]:捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数,捕捉列表能够捕捉上下文中的变量供lambda使用。
  • (parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略。
  • mutable:默认情况下,lambda函数总是一个const函数,添加mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
  • -> return-type:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导。
  • {statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。

注:在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此C++11中最简单的lambda函数为:[]{}; 但该lambda函数不能做任何事情。

// 最简单的lambda表达式, 该lambda表达式没有任何意义
[] {};

// 省略参数列表和返回值类型,返回值类型由编译器推导为int
int a = 3, b = 4;
[=] {return a + 3; };

// 省略了返回值类型,无返回值类型
auto fun1 = [&](int c) {b = a + c; };
fun1(10);
cout << a << " " << b << endl << endl;

// 各部分都很完善的lambda函数
auto fun2 = [=, &b](int c)->int {return b += a + c; };
cout << fun2(10) << endl << endl;

// 复制捕捉x
int x = 10;
auto add_x = [x](int a) mutable { x *= 2; return a + x; };
cout << add_x(10) << endl;// 30
cout << x << endl;// 仍然是10

注:mutable使被捕获的变量可变其实比较迷惑,虽然可变,但其并不是传递过来x的引用,并不会影响lambda函数外部的x。

在这里插入图片描述
捕获列表说明:
捕捉列表描述了上下文中哪些数据可以被lambda使用,以及使用的方式为传值还是传引用。

  • [var]:表示值传递方式捕捉变量var
  • [=]:表示值传递方式捕获所有父作用域和全局域中的变量(包括this)
  • [&var]:表示引用传递捕捉变量var
  • [&]:表示引用传递捕捉所有父作用域和全局域中的变量(包括this)
  • [this]:表示值传递方式捕捉当前的this指针

⭐注:

  1. 父作用域指包含lambda函数的语句块
  2. 语法上捕捉列表可由多个捕捉项组成,以逗号分割
    如:[=, &a, &b]:以传引用传递方式捕捉变量a和b,传值方式捕捉其他所有变量;[&, a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量。
  3. 捕捉列表不允许变量重复传递,否则会导致编译错误
    如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复
  4. 在块作用域以外的lambda函数捕捉列表必须为空
  5. 在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者非局部变量都会导致编译报错。
  6. lambda表达式之间不能相互赋值,即使看起来类型相同
  7. 成员函数中的lambda对象,默认捕捉一个this指针
void (*PF)();
int main()
{
	auto f1 = [] {cout << "hello world" << endl; };
	auto f2 = [] {cout << "hello world" << endl; };
	// 此处先不解释原因,等lambda表达式底层实现原理看完后,大家就清楚了
	//f1 = f2; // 编译失败--->提示找不到operator=()
	// 允许使用一个lambda表达式拷贝构造一个新的副本
	auto f3(f2);
	f3();
	// 可以将lambda表达式赋值给相同类型的函数指针
	PF = f2;
	PF();
	return 0;
}

在这里插入图片描述

函数对象和lambda表达式

函数对象,又称为仿函数,即可以想函数一样使用的对象,就是在类中重载了operator()运算符的类对象。

class Rate
{
public:
	Rate(double rate) : _rate(rate)
	{}
	double operator()(double money, int year)
	{
		return money * _rate * year;
	}
private:
	double _rate;
};
int main()
{
	// 函数对象
	double rate = 0.49;
	Rate r1(rate);
	r1(10000, 2);
	// lambda
	auto r2 = [=](double monty, int year)->double {return monty * rate * year;
		};
	r2(10000, 2);
	return 0;
}

从使用方式上来看,函数对象与lambda表达式完全一样。
函数对象将rate作为其成员变量,在定义对象时给出初始值即可,lambda表达式通过捕获列表可以直接将该变量捕获到。
如果我们转到底层去观察其汇编代码:
在这里插入图片描述
会发现,实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,即:如果定义了一个lambda表达式,编译器会自动生成一个类,在该类中重载了operator()。

🔥包装器

function

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。在这里插入图片描述

// std::function在头文件<functional>中

// 以下是模板原型
template <class T> function;     // undefined
template <class Ret, class... Args> class function<Ret(Args...)>;

// 模板参数说明:
// Ret包装可调用对象的返回值类型
// Args...为可变参数模板(里面是被调用函数的形参)

关于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;
	
	// lamber表达式
	std::function<int(int, int)> func3 = [](const int a, const int b)
		{return a + b; };
	cout << func3(1, 2) << endl;
	
	// 类的成员函数
	// plusi为静态成员函数,取函数指针时可不加&符号
	std::function<int(int, int)> func4 = &Plus::plusi;
	cout << func4(1, 2) << endl;
	// plusd为非静态成员函数,取函数指针时需要加&符号(规定)
	// 由于的成员函数隐含this指针,所以需要多传一个参数
	// std::function<double(Plus*, double, double)> func5 = &Plus::plusd;
	// Plus pd;
	// cout << func5(&pd, 1.1, 2.2) << endl;
	// 以上三行和下面两行代码是同样效果
	std::function<double(Plus, double, double)> func5 = &Plus::plusd;
	cout << func5(Plus(), 1.1, 2.2) << endl;
	// 传入Plus是转换调用,不需要完全和this匹配
	return 0;
}

在这里插入图片描述

bind

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

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为第二个参数,以此类推。_1,_2 … _n为命名空间placeholders内的标识符。

使用举例:

#include <functional>
int Plus(int a, int b, int c)
{
	return a + b + c;
}
class Sub
{
public:
	int sub(int a, int b)
	{
		return a - b;
	}
};
int main()
{
	//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定
	std::function<int(int, int, int)> func1 = std::bind(Plus, placeholders::_1,
		placeholders::_2, placeholders::_3);
	//auto func1 = std::bind(Plus, placeholders::_1, placeholders::_2, placeholders::_3);
	//表示绑定函数 plus 的第一,二为: 1, 2
	auto func2 = std::bind(Plus, 1, 2, placeholders::_1);
	cout << func1(1, 2, 3) << endl;
	cout << func2(4) << endl;

	Sub s;
	// 绑定成员函数
	std::function<int(int, int)> func3 = std::bind(&Sub::sub, s,
		placeholders::_1, placeholders::_2);
		
	// 参数调换顺序
	std::function<int(int, int)> func4 = std::bind(&Sub::sub, s,
		placeholders::_2, placeholders::_1);
	cout << func3(1, 2) << endl;
	cout << func4(1, 2) << endl;
	return 0;
}

在这里插入图片描述

bind一般用于绑死一些固定参数。
bind的本质其实是返回一个仿函数对象。

🌈结语

本篇博客首先补充了一下C++11新增的两个默认成员函数,移动构造和移动赋值重载,可变参数模板以及STL容器中的emplace接口。然后讲解了lambda语法,以及包装器function和bind。lambda语法使程序员从每次调用相关算法都需要实现一个类的冗余工作中摆脱;包装器包装可调用对象的特性也使C++程序变得更加灵活。

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

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

相关文章

论文翻译:Benchmarking Large Language Models in Retrieval-Augmented Generation

https://ojs.aaai.org/index.php/AAAI/article/view/29728 检索增强型生成中的大型语言模型基准测试 文章目录 检索增强型生成中的大型语言模型基准测试摘要1 引言2 相关工作3 检索增强型生成基准RAG所需能力数据构建评估指标 4实验设置噪声鲁棒性结果负面拒绝测试平台结果信息…

算法5:位运算

文章目录 小试牛刀进入正题 没写代码的题&#xff0c;其链接点开都是有代码的。开始前请思考下图&#xff1a; 小试牛刀 位1的个数 class Solution { public:int hammingWeight(int n) {int res 0;while (n) {n & n - 1;res;}return res;} };比特位计数 class Solution…

计算机毕业设计选题推荐-猫眼电影数据可视化分析-Python爬虫-k-means算法

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

进程和文件痕迹排查——LINUX

目录 介绍步骤 介绍 进程&#xff08;Process&#xff09;是计算机中的程序关于某数据集合上的一次运行活动&#xff0c;是系统进行资源分配和调度的基本单位&#xff0c;是操作系统结构的基础。 在早期面向进程设计的计算机结构中&#xff0c;进程是程序的基本执行实体&…

fastadmin 安装

环境要求&#xff0c;大家可以参考官方文档的&#xff0c;我这里使用的是phpstudy&#xff0c;很多已经集成了。 注意一点&#xff0c;PHP 版本&#xff1a;PHP 7.4 。 第二步&#xff1a;下载 下载地址&#xff1a;https://www.fastadmin.net/download.html 进入下载地址后…

IDEA:Terminal找不到npm

Terminal的命令失效通过修改cmd.exe的方式还是不生效的话&#xff0c;考虑是windwos11 默认idea不是通过管理员启动的&#xff0c;如下图修改就可以了。

前端vue 3中使用 顶象 vue3 版本

顶象 验证 的插件 不知道大家使用过没有 顶象-业务安全引领者&#xff0c;让数字世界无风险 可以防止 机器人刷接口 等 可以在任何 加密操作中使用 下面我直接 贴代码 解释 <script src"https://cdn.dingxiang-inc.com/ctu-group/captcha-ui/v5/index.js" cro…

第12章 网络 (2)

目录 12.5 网络命名空间 12.6 套接字缓冲区 12.6.1 使用 sk_buff 管理数据 12.6.2 管理套接字缓冲区数据 本专栏文章将有70篇左右&#xff0c;欢迎关注&#xff0c;查看后续文章。 12.5 网络命名空间 一个网卡可能只在某个特定命名空间可见。 struct net&#xff1a; 表…

C语言贪吃蛇之BUG满天飞

C语言贪吃蛇之BUG满天飞 今天无意间翻到了大一用C语言写的贪吃蛇&#xff0c;竟然还标注着BUG满天飞&#xff0c;留存一下做个纪念&#xff0c;可能以后就找不到了 /* 此程序 --> 贪吃蛇3.0 Sur_流沐 当前版本&#xff1a; Bug满天飞 */ #include<stdio.h> #includ…

Linux C、C++编程之线程同步

【图书推荐】《Linux C与C一线开发实践&#xff08;第2版&#xff09;》_linux c与c一线开发实践pdf-CSDN博客《Linux C与C一线开发实践&#xff08;第2版&#xff09;&#xff08;Linux技术丛书&#xff09;》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com…

qt处理表格,Qtxlsx库文件的安装以及导入

qt想要处理excel表格的&#xff0c;这个过程中避免不了使用Qtxlsx这个库文件。这几天花了几天时间&#xff0c;终于本地调通了。记录一下。 关于Qtxlsx的使用&#xff0c;大致分为2中方法。 方法一&#xff1a;直接下载对应的xlsx文件&#xff0c;然后在.pro文件中 这种方法是…

使用Java往Geoserver发布tif图层和shp图层

1. Maven依赖 栅格文件对应Tif文件 (即: 栅格就是tif) 矢量文件对应shp文件(即: 矢量就是shp) 注: 有的依赖可能在中央仓库及一些镜像仓库找不到需要手动指定仓库, 在依赖最下方 <!-- 中文转拼音工具类 --><dependency><groupId>com.belerweb</groupId&g…

指针的学习和理解

初级 1、指针的概念 在64位操作系统中&#xff0c;不管什么类型的指针都占8个字节 int a1; int* p&a;//p就是一个整型的指针&#xff0c;保存了a的地址2、指针和变量 int* p&a;* p100; // 等价于a100p //p&a*有两种定义&#xff1a; 定义的时候&#xff08;前…

【工具类】Java优雅的将XML转为JSON格式、XML转JSON

Java优雅的将XML转为JSON格式、XML转JSON 1. 导入依赖1.1 Maven使用1.2 Gradle使用 2. 代码编写3.运行示例 1. 导入依赖 1.1 Maven使用 <dependency><groupId>org.dom4j</groupId><artifactId>dom4j</artifactId><version>2.1.3</vers…

TCP连接过程

文章目录 TCP连接过程 附录TCP报文中关键术语字段 后面再完整出理论、出实战、出总结 TCP连接过程 三次握手&#xff08;Three-Way Handshake&#xff09;过程。 TCP抓包结果分析&#xff1a; step1&#xff1a;Client1客户端--->Server1服务器发送SYN&#xff08;同步…

【C++二分查找 前缀和】1658. 将 x 减到 0 的最小操作数

本文涉及的基础知识点 C二分查找 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 LeetCode1658. 将 x 减到 0 的最小操作数 给你一个整数数组 nums 和一个整数 x 。每一次操作时&#xff0c;你应当移除数组 nums 最左边或最右边的元素&am…

MambaCSR: 使用SSM的双交错扫描压缩图像超分辨率

MambaCSR: Dual-Interleaved Scanning for Compressed Image Super-Resolution With SSMs 2408.11758 (arxiv.org) GitHub - renyulin-f/MambaCSR: The code source of MambaCSR 摘要 本文提出了MambaCSR&#xff0c;这是一个基于Mamba的简单但有效的框架&#xff0c;用于解决…

ffmpeg读取时长、读取视频格式

ffmpeg读取时长、读取视频格式 ffmpeg读取时长ffmpeg读取视频格式 ffmpeg读取时长 命令命令介绍具体用法ffmpeg -i查看视频时长ffmpeg -i 视频链接 or 视频路径 2>&1 | grep Duration ffmpeg读取视频格式 命令命令介绍具体用法ffmpeg -i查看视频时长ffmpeg -i 视频链接…

集合及数据结构第八节(下)———— 队列(Queue)、队列的模拟实现和练习

系列文章目录 集合及数据结构第八节&#xff08;下&#xff09;———— 队列(Queue)、队列的模拟实现和练习 队列(Queue)、队列的模拟实现和练习 队列的概念队列的使用队列模拟实现循环队列双端队列练习题 文章目录 系列文章目录集合及数据结构第八节&#xff08;下&#x…

Chainlit接入DifyAI知识库接口快速实现自定义用户聊天界面

前言 由于dify只提供了一个分享用的网页应用&#xff0c;网页访问地址没法自定义&#xff0c;虽然可以接入NextWeb/ChatGPT web/open webui等开源应用。但是如果我们想直接给客户应用&#xff0c;还需要客户去设置配置&#xff0c;里面还有很多我们不想展示给客户的东西怎么办…