C++核心编程——详解函数模板

在这里插入图片描述
纵有疾风起,人生不言弃。本文篇幅较长,如有错误请不吝赐教,感谢支持。

💬文章目录

    • 一.模板的基础知识
      • ①为什么有模板?
      • ②初识模板
    • 二.函数模板
      • ①函数模板的定义
      • ②函数模板的使用
      • ③函数模板实例化
        • 1️⃣隐式实例化
        • 2️⃣显示实例化
      • ④函数模板的具体化(特化、特例化)
        • 1️⃣模板的局限性及解决方法
        • 2️⃣函数模板具体化(特化、特例化)
        • 3️⃣实例化和具体化的相同点
      • ⑤普通函数和函数模板调用规则
      • ⑥函数模板的注意事项
      • ⑦函数模板的分文件编写
    • 三.函数模板高级
      • ①decltype关键字

一.模板的基础知识

①为什么有模板?

在C++程序中,声明变量、函数、对象等实体时,程序设计者需要指定数据类型,让编译器在程序运行之前进行类型检查并分配内存,以提高程序运行的安全性和效率。但是这种强类型的编程方式往往会导致程序设计者为逻辑结构相同而具体数据类型不同的对象编写模式一致的代码
🃏案例:

//交换int数据
void SwapInt(int& a,int& b)
{
    int temp = a;
    a = b;
    b = temp;
}
//交换char数据
void SwapChar(char& a,char& b)
{
    char temp = a;
    a = b;
    b = temp;
}

问题:如果我要交换double类型数据,那么还需要些一个double类型数据交换的函数,繁琐,写的函数越多,当交换逻辑发生变化的时候,所有的函数都需要修改,无形当中增加了代码的维护难度,如果能把类型作为参数传递进来就好了,传递int就是int类型交换,传递double类型就是double类型交换。

②初识模板

C++标准提供了模板机制,用于定义数据类型不同但逻辑结构相同的数据对象的通用行为。在模板中,运算对象的类型不是实际的数据类型,而是一种参数化的类型。带参数类型的函数称为函数模板,带参数类型的类称为类模板

例如上面的案例,我们可以定义函数Swap(),交换两个参数,我们可以将类型参数化,如Swap(T,T),其中,T就是参数化的类型,在调用Swap()函数时,可以传入任意类型的数据,函数可以根据传入的数据推导出T的值是哪种数据类型,从而进行相应的操作。这样程序设计者就可以专注于逻辑代码的编写,而不用关心实际具体的数据类型。

模板就像生产模具,例如,中秋生产月饼,生产月饼的模具就是模板,在做模具时,只关心做出什么样式的月饼,而不用关心月饼具体的原料是什么(如水果、五仁、豆沙等)。
在这里插入图片描述
程序运行时,模板的参数由实际参数的数据类型决定,编译器会根据实际参数的数据类型生成相应的一段可运行代码,这个过程称为模板实例化。函数模板生成的实例称为模板函数,类模板生成的实例称为模板类

二.函数模板

函数模板是函数的抽象,它与普通函数相似,唯一的区别就是函数参数的类型是不确定的,函数参数的类型只有在调用过程中才被确定。

①函数模板的定义

C++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数,其函数类型和形参类型不具体制定(泛型),用一个虚拟的类型来代表。这个通用函数就成为函数模板。凡是函数体相同的函数都可以用这个模板代替,不必定义多个函数,只需在模板中定义一次即可。 在调用函数时系统会根据实参的类型来取代模板中的虚拟类型,从而实现不同函数的功能
定义函数模板的语法格式如下所示:在这里插入图片描述
上述语法格式中,template是声明模板的关键字,<>中的参数称为模板参数;typename关键字用于标识模板参数,可以用class关键字代替,class和typename并没有区别。

  • 模板参数< >中不能为空,一个函数模板中可以有多个模板参数,模板参数和普通函数参数相似,只是不确定数据类型。
  • template下面是定义的函数模板,函数模板定义方式与普通函数定义方式相同只是参数列表中的数据类型要使用<>中的模板参数名表示 。

②函数模板的使用

template<class T>//T是模板参数
void mySwap(T &a,T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
}
//使用函数模版,和正常调用函数没区别
void test()
{
	int a = 10,b = 20;
    double c=3.1415,d=9.26;
	//1.编译器会根据实参来自动推导数据类型,并生成相应的函数
	mySwap(a,b);//mySwap中的T会变成int类型
	cout << "a=" << a <<"b=" << b << endl;
	mySwap(c,d);//mySwap中的T会变成double类型
    cout << "c=" << c << "d=" << d << endl;
}

在这里插入图片描述

🖲解释:

  • 第一行指出,要建立一个模板,并将类型命名为T。关键字template和class是必需的,也可以使用关键字typename代替class.另外,必须使用尖括号<>。类型名可以任意选择(这里为T)。
  • 余下的代码交换两个T值的算法。模板并不创建任何函数,而只是告诉编译器如何定义函数。需要交换int的函数时,编译器将按模板模式创建这样的函数,并用int代替T。同样,需要交换double的函数时,编译器将按模板模式创建这样的函数,并用double代替T.

③函数模板实例化

所谓函数模板实例化,就是用类型参数替换函数模板中的模板参数,生成具体类型的函数定义。
实例化可分为:

  • 隐式实例化
  • 显式实例化

下面分别介绍这两种实例化方式。

1️⃣隐式实例化

隐式实例化是根据函数调用时传入的参数的数据类型确定模板参数T的类型,模板参数的类型是隐式确定的。
例如下例调用mySwap()函数模板时,传入的是int类型数据a和b,编译器根据传入的实参推演出模板参数T的类型是int,就会根据函数模板将T全部替换成int,实例化出一个int类型的函数,如下所示:

//交换int数据
void mySwap(int& a,int& b)
{
    int temp = a;
    a = b;
    b = temp;
}
int main()
{
    int a=20,b=30;
    mySwap(a,b);
    return 0;
}

编译器生成具体类型函数的这一过程就称为实例化,生成的函数称为模板函数
需要注意的是:

①不能在函数调用的参数中指定模板参数的类型,对函数模板的调用应使用实参推演。例如,只能进行Swap(2,3)这样的调用,或者定义整型变量int a=20,b=30,再将变量a、b作为参数,进行Swap(a,b)这样的调用,编译器会根据传入的实参推演出T为int类型,而不能使用Swap(int,int)方式,直接将类型传入进行调用。
②函数模板不会减少可执行程序的大小,因为编译器会根据调用时的参数类型进行相应的实例化。

2️⃣显示实例化

隐式实例化不能为同一个模板参数指定两种不同的类型。
例如:

template<class T>//T是模板参数
void mySwap(T &a,T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
}
//就一个模板参数T,你隐式的给传两个数据类型
Swap(10,3.14)//编译器搞不清楚T是什么数据类型,二义性。
//那就让我们显示的告诉它

函数参数类型不一致,编译器便会报错。这就需要显式实例化解决类型不一致的问题。显式实例化需要指定函数模板中的模板参数的数据类型,语法格式如下所示:
在这里插入图片描述
< >中是显式实例化的数据类型,即要实例化出一个什么类型的函数。相当于主动告诉编译器,我希望你将用我传的参数的数据类型去将模板参数显示转换为该类型,并按照模版规定的逻辑生成函数定义。
<实例化类型>尖括号中实例化类型数量要与你定义模板时定义模板参数数量一致,可以理解用你显示具体化的数据类型去替换你定义模板时模板参数。
🃏案例:

template<class T>
void mySwap(T& a, T& b)
{
	T tmp = a;
	a = b;
	b = tmp;
}
template void mySwap<int>(int& a, int& b);//int去替换定义函数模板时的T
void test()
{
	int a = 10,b= 3;
	mySwap<int>(a, b);//显示告诉模板mySwap中的T会变成int类型
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
}

🖲代码运行结果如下:
在这里插入图片描述

④函数模板的具体化(特化、特例化)

1️⃣模板的局限性及解决方法

假设有如下模板函数:

template<class T>
void func(T a, T b)
{}

如果代码实现时定义了赋值操作 a = b,但是T为数组,这种假设就不成立了同样,如果里面的语句为判断语句 if(a>b),但T如果是结构体,该假设也不成立,另外如果是传入的数组,数组名为地址,因此它比较的是地址,而这也不是我们所希望的操作。总之,编写的模板函数很可能无法处理某些类型,另一方面,有时候通用化是有意义的,但C++语法不允许这样做。为了解决这种问题,可以提供模板的重载,为这些特定的类型提供显示具体化的模板

2️⃣函数模板具体化(特化、特例化)

函数模板的特点是通用性,可以解决某个方面的普遍性问题,但是这个世界上的事情从来不是绝对的,有普遍的,就有特殊的。

可以提供一个具体化的函数定义,当编译器找到与函数调用匹配的具体化定义时,将使用该定义,不再寻找模板,函数模板是对函数体重新定义。

具体化(特例化、特化)的语法:
在这里插入图片描述
注意显示实例化的前缀是template,没有尖括号<>。
举例:交换两个学生类的成员变量。

#include <iostream>
using namespace std;
//函数模板
template<class T>
void mySwap(T &a,T &b)
{
    T tmp = a;
    a = b;
    b = tmp;
    cout<<"调用了mySwap(T &a,T &b)"<<endl;
}
class Student
{
public:
    Student(int num):score(num){}
    int score;
};
//具体化函数模板,为Student这一数据类型单独写一份函数定义
template <>void mySwap<Student>(Student &a,Student &b)
{
     int tmp = a.score;
     a.score = b.score;
     b.score = tmp;
     cout<<"具体化函数模板:template <>void mySwap<Student>(Student &a,Student &b)调用"<<endl;
 }
int main()
{
    int a = 10,b = 20;
    mySwap(a,b);//调用函数模板
    cout << "a=" << a << "b=" << b << endl;
    Student s1(150),s2(90);
    mySwap(s1,s2);//调用具体化函数模板
    cout << "s1.score" << s1.score<< "s2.score" << s2.score << endl;
    return 0;
}

注意:具体化函数的返回值、函数名和形参列表与函数模板相同。但是对于具体化函数模板,不管是函数声明还是定义中,都是具体的数据类型,没有通用的数据类型了。
在这里插入图片描述

3️⃣实例化和具体化的相同点

实例化和具体化都是用于生成具体的定义,实例化是编译器借助你提供的数据类型来隐式或显示替换模板中的模板参数来实例化出函数定义,而具体化则是你专门为一个具体的数据类型写函数定义。
例如:
显示实例化

template void Swap<int>(int&a,int&b)//显示具体化
//使用Swap()模板生成int类型的函数定义
//就是编译器借助你提供的int去显示替换模板中的模板参数
//生成int函数定义类型。

显示具体化

template<>void Swap <int>(int&a,int&b)//显示具体化
//不要使用Swap( )模板来生成函数定义
//而应使用专门为int类型显式地定义的函数定义

⑤普通函数和函数模板调用规则

  • ✅如果函数有多个原型,则编译器在选择函数调用时,普通函数优先于模板函数,显式具体化模板优先于函数模板。
  • ✅如果希望使用函数模板,可以用空模板参数强制使用函数模板。
  • ✅如果函数模板可以产生一个更好的匹配,那么选择模板。
#include <iostream>
using namespace std;
//普通函数
void mySwap(int a, int b)
{
    int tmp = a;
    a = b;
    b = tmp;
    cout << "调用了普通函数mySwap(int a,int b)" << endl;
}
//函数模板
template<class T>
void mySwap(T& a, T& b)
{
    T tmp = a;
    a = b;
    b = tmp;
    cout << "调用了函数模板mySwap(T &a,T &b)" << endl;
}
class Student
{
public:
    Student(int num) :score(num) {}
    int score;
};
//显示具体化
template <>void mySwap<Student>(Student& a, Student& b)
{
    int tmp = a.score;
    a.score = b.score;
    b.score = tmp;
    cout << "具体化函数模板:template <>void mySwap<Student>(Student &a,Student &b)调用" << endl;
}
int main()
{
    int a = 10, b = 20;
    //如果函数模板和普通函数都能匹配,c++编译器优先考虑普通函数
    mySwap(a, b);
    //如果我必须要调用函数模板,那么怎么办?
    //可以通过空模板实参列表的语法限定编译器只能通过模板匹配
    mySwap<>(a, b);
    cout << "a=" << a << "b=" << b << endl;
    Student s1(150), s2(90);
    //调用显示具体化函数模板
    mySwap(s1, s2);
    cout << "s1.score:" << s1.score << "s2.score:" << s2.score << endl;
    //此时普通函数和显示具体化模板都没有很好的匹配
    //所以使用函数模板可以产生一个更好的匹配,那么选择模板
    char d = 'd', e = 'e';
    mySwap(d, e);
    return 0;
}

在这里插入图片描述

⑥函数模板的注意事项

1️⃣可以为类的成员函数创建模板,但不能是虚函数和析构函数。

#include <iostream> 
#include<string> 
using namespace std;
class Maker
{
public:
template<typename T>
Maker(T a)
{
    cout<<a<<endl;
}
template<typename T>
void show(T b)
{
    cout<<b<<endl;
}
};
int main()
{
    Maker m1("感谢支持强风吹拂king的博客");
    m1.show(666);
}

上例将Maker的构造函数和成员函数show声明成函数模板,可以接受任意的数据类型。
在这里插入图片描述

如果将show声明为虚函数会怎么样呢?
在这里插入图片描述
也不能把析构函数声明成函数模板,一个类的析构函数只有一个,根本没有参数,不需要模板。

2️⃣只要使用函数模板,就必须明确数据类型(不论是显示实例化还是隐式实例化),让编译器知道模板参数到底是个什么数据类型,确保实参与函数模板能匹配上,生成具体的函数定义。

举例:

template<class T>//T是模板参数
void mySwap(T &a,T &b)
{
	T tmp = a;
	a = b;
	b = tmp;
}
//使用函数模版
int main()
{
    int a = 10,b = 20;
	mySwap(a,b);
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
}

现在mySwap函数描述了一种通用类型T,函数这两个形参都是T定义的,也就是说这两个形参的数据类型必须相同,在main函数中,a和b都是int类型没有任何问题。
在这里插入图片描述

int main()
{
    int a = 10
    char b = 20;
	mySwap(a,b);
	cout << "a=" << a << endl;
	cout << "b=" << b << endl;
}

如果将b改为字符,编译器报错了,模板参数T不明确,未能找到匹配的重载函数。
在这里插入图片描述
实参与函数模板的匹配,和函数重载道理一致,如果匹配不上就会报错。
把函数模板的形参都删掉,函数里面的代码也删掉。现在mySwap函数没有参数,那么编译还能通过吗?

template<class T>//T是模板参数
void mySwap()
{
	cout<<"show函数被调用"<<endl;
}
//使用函数模版
int main()
{
	mySwap();
}

放图:

未能推导模板参数。
也就是说模板参数很死板,一定要明确数据类型,上面无参show函数,不能通过参数隐式实例化函数模板,那就只能显示实例化。

mySwap<int>();

在这里插入图片描述

3️⃣使用函数模板时,如果是隐式实例化函数模板,不会发生隐式类型转换,如果显式实例化函数模板,则可以发生隐式类型转换。

举例:

template<class T>//T是模板参数
T Add (T a,T b)
{
	return a+b;
}
//使用函数模版
int main()
{
    int a = 10,b = 20;
    cout<<Add(a,b)<<endl;
}

现在a和b都是整型,把他们作为函数模板的实参,编译器推导出来的就是整型,与函数模板的定义是匹配的。
在这里插入图片描述

我们将b改为字符,在C++中字符是可以隐式转换成整型的。

int main()
{
    int a = 10;
    char b = 20;
    cout<<Add(a,b)<<endl;
}

在这里插入图片描述
模板参数不明确,看来隐式实例化没有将字符b转换成整型。
那我们显示实例化试一下:

int main()
{
    int a = 10;
    char b = 'a';
    cout<<Add<int>(a,b)<<endl;
}

在这里插入图片描述

编译成功。也就是说如果显式实例化函数模板,则可以发生隐式类型转换。

4️⃣如果函数模板有多个模板参数,则每个模板参数前都必须使用关键字class或typename修饰。

例如:

template<typename T, typename U>//两个关键字可以混
void func(T t, U u){}
template<typename T,U>//错误,每一个模板参数前都必须有关键字修饰
void func(T t, U u){}

5️⃣全局作用域中声明的与模板参数同名的对象、函数或类型,在函数模板中将被隐藏。

例如:

int num=9000; 
template<typename T> 
void func(T t)
{
    T num;   
    cout<<num<<endl;//输出的是局部变量num,全局int类型的num被屏蔽
}

在函数体内访问的num是T类型的变量num,而不是全局int类型的变量num。

6️⃣函数模板中声明的对象或类型不能与模板参数同名。

例如:

template<typename T>
void func(T t){
    typedef float T;//错误,定义的类型与模板参数名相同 } ​​

7️⃣模板参数名在同一模板参数列表中只能使用一次,但可在多个函数模板声明或定义之间重复使用。

例如:

template<typename T, typename T>//错误,在同一个模板中重复定义模板参数 
void func1(T x, T y){}template<typename T>void func2(T z){}template<typename T>//在不同函数模板中可重复使用相同的模板参数名 void func3(T w){}​​

⑦函数模板的分文件编写

函数模板只是函数的描述,没有实体,创建函数模板的代码放在头文件中。
函数模板的具体化有实体,编译的原理和普通函数一样,所以,声明放在头文件中,定义放在源文件中。
例如把下面这段代码分文件编写:

#include <iostream>
using namespace std; 
void Swap(int a, int b) // 普通函数。
{
cout << "使用了普通函数。\n";
}
template <typename T>
void Swap(T a, T b) // 函数模板。
{
cout << "使用了函数模板。\n";
}
template <>
void Swap(double a, double b) // 函数模板的具体化版本。
{
cout << "使用了具体化的函数模板。\n";
}
int main()
{
    Swap(1,2); //将使用普通函数。
    Swap(1.3, 3.5); //将使用具体化的函数模板。
    Swap('c', 'd'); //将使用函数模板。
}

在这里插入图片描述

头文件public.h,存放函数的声明

#pragma once
#include <iostream>
using namespace std; 
void Swap(int a, int b); //普通函数的声明
template <typename T>
void Swap(T a, T b) // 函数模板的定义
{
  cout << "使用了函数模板。\n";
}
template <>
void Swap(double a, double b); // 函数模板的具体化的声明

public.cpp,存放函数的实现

#include "public.h"
void Swap(int a, int b) //普通函数的实现
{
    cout << "使用了普通函数。\n";
}
template <>
void Swap(double a, double b) // 函数模板的具体化的实现
{
    cout << "使用了具体化的函数模板。\n";
}

demo01.cpp 测试文件

#include "public.h"
int main()
{
    Swap(1,2); // 将使用普通函数。
    Swap(1.3, 3.5); // 将使用具体化的函数模板。
    Swap('c', 'd'); // 将使用函数模板。
}

三.函数模板高级

①decltype关键字

先来看一个示例:

template<typename T1,typename T2>
void func(T1 a, T2 b)
{
    //其他代码
    ??? tmp=a+b;
    //其他代码
}

func函数模板描述了两个通用类型T1和T2,形参a和b分别用T1和T2定义,在函数体内声明tmp保存a+b的结果,那么tmp怎么声明,用什么数据类型,T1还是T2,还是其他?

如果T1是double,T2是int,那么最终结果是double类型;如果T1是short,T2是int,那么最终结果是int类型;还有除了C++的内置数据类型,结构体和类,还可以重载+号运算符,这样情况就更复杂了,这样的话tmp又将定义成什么类型呢?

在C++11中,decltype操作符,用于查询表达式的数据类型。
语法:

decltype(表达式) var;
decltype分析表达式并返回它的类型,
用这个类型定义var变量。

decltype分析表达式并返回它的类型,不会计算执行表达式。函数调用也一种表达式,因此不必担心在使用decltype时执行了函数。
decltype推导规则(按步骤):

  • 1)如果表达式是一个没有用括号括起来的标识符,decltype本身自带的()不算,则var的类型与该标识符的类型相同,包括const等限定符。
int main()
{
    const short a = 100;
    decltype(a) var=666;
    //var的类型将和a保持一致。
    //注意:如果decltype分析表达式的类型是引用
    //那么必须初始化这个引用
    short b = 10;
    short& pa = b;
    decltype(pa) var2 = b;//var2的类型是short&,初始化var2的引用指向b;、
    cout << "var:" << var <<"var2:" << var2 << endl;
}

在这里插入图片描述
在这里插入图片描述

  • 2)如果表达式是一个函数调用,则var的类型与函数的返回值类型相同(函数不能返回void,但可以返回void * ,因为void * 可以声明变量)。
int func()
{
    cout<<"调用了func函数"<<endl;
    return 666;
}
int main()
{
    int b=100;
    decltype(func()) val=b;
}

大家看,val的类型,是func函数的返回类型int。
在这里插入图片描述

,我们运行代码,发现并没有调用func函数,这也说明decltype不会计算执行表达式。
在这里插入图片描述

✅还有一点:
在decltype中填函数名和函数调用是两回事。

int main()
{
    decltype(func()) val=b;
    //填函数调用,得到的是函数的返回类型
    decltype(func) *val2=func;
    //只填函数名得到的是函数的类型
    //再加*号就是函数指针类型,我们让val2指向func函数
    val2();//借助函数指针val2调用func函数
}

在这里插入图片描述
在这里插入图片描述

  • 3)如果表达式是一个左值(能取地址)(要排除第一种情况)、或者用括号括起来的标识符,那么var的类型是表达式的引用。
int func()
{
    cout << "调用了func函数" << endl;
    return 666;
}
int main()
{
    short a = 100;
    decltype(a) var;
    //var的类型将和a保持一致。
    decltype((a)) var2=a;//此时的a用括号括起来了,那么var2就是short&类型
    decltype((func)) val3 = func;
    //如果func不加(),那val3就是函数类型
    //但加了()就是函数类型的引用
    val3();//函数类型引用也可以调用函数。
}

在这里插入图片描述
在这里插入图片描述

  • 4)如果上面的条件都不满足,则var的类型与表达式的类型相同。
    如果需要多次使用decltype,可以结合typedef和using,给数据类型起别名。
    那么我们现在用decltype关键字去解决最初的问题
template<typename T1,typename T2>
void func(T1 a, T2 b)
{
    //其他代码
    decltype(a+b) tmp=a+b;
    cout<<"tmp="<<tmp<<endl;
    //其他代码
}
int main()
{
    func(3.14,600);
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

泰坦陨落2找不到msvcr120.dll的解决方法

msvcr120.dll是的动态链接库文件之一。它在Windows操作系统中发挥着重要的作用&#xff0c;它提供了应用程序所需的各种功能和方法。 该文件返回编译后的代码所需的支持库。msvcr120.dll包含用于C / C编译器生成的应用程序所需的重要功能&#xff0c;包括数学函数&#xff0c;…

C++学习 数据类型

数据类型存在的意义&#xff1a; 给变量分配合适的内存空间&#xff0c;避免资源浪费。 整型&#xff1a; 整型变量表示的是整数类型的数据 long类型 在 windows 中4字节 linux 中 32位4字节 64位8字节&#xff0c;占用空间的不同&#xff0c;可以表示的取值范围就越广&…

python:基础知识—流程控制—函数与模块—数据结构—类与GUI和Turtle—异常处理与文件,概括全书(上万字最详细版)

这里是一张夜景&#xff0c;给大家放松一下。 !&#xff01;无锡南长街 文章目录 模块一&#xff1a;基础知识1、python语言2、常见数字类型3、字符串4、数字类型转换5、标识符命名6、常见关键字7、运算符与表达式&#xff08;1&#xff09;算术运算符&#xff08;2&#xff09…

ZooKeeper的集群部署和启动与关闭

ZooKeeper是一个分布式应用程序协调服务。一个ZooKeeper集群可以存在多个Follower和Observer服务器&#xff0c;但只允许存在一台Leader服务器。如果Leader服务器宕机&#xff0c;那么ZooKeeper集群的其它服务器会投票选举出一个新的Leader服务器&#xff0c;为防止投票数不过半…

Elasticsearch:部署 ELSER - Elastic Learned Sparse EncoderR

警告&#xff1a;此功能处于技术预览阶段&#xff0c;可能会在未来的版本中更改或删除。 Elastic 将尽最大努力修复任何问题&#xff0c;但技术预览中的功能不受官方 GA 功能的支持 SLA 约束。 Elastic Learned Sparse EncodeR - 或 ELSER - 是由 Elastic 训练的检索模型&#…

浅谈中移链中插件的功能及使用

中移链是在满足我国信息化监管需求、合规可控的前提下&#xff0c;打造的中国移动区块链服务平台。它允许使用插件来扩展其功能&#xff0c;以适应各种不同的使用场景。 什么是中移链插件呢&#xff1f;如果把中移链比作一个操作系统&#xff0c;那么插件就类比于操作系统上的…

工作流引擎Flowable

这里写目录标题 1.Flowable基础1.1 入门学习 2.流程图设计器2.1 FlowableUI2.1.1 绘制流程图 1.Flowable基础 官方手册 1.1 入门学习 一、依赖 <dependencies><dependency><groupId>org.flowable</groupId><artifactId>flowable-engine</…

Android系统安全 — 6.2 Ethernet安卓架构

1. Android Ethernet架构介绍 整个Ethernet系统架构如下图所示&#xff1a; 以太网服务&#xff08;EthernetService&#xff09;的启动与注册流程&#xff1b;应用层调用使能ethernet功能的方法流程来分析&#xff0c;从应用层如何将指令一步一步传到底层kernel&#xff1b;…

阿里巴巴开源的Spring Cloud Alibaba手册在GitHub上火了

“微服务架构经验你有吗&#xff1f;” 前段时间一个朋友去面试&#xff0c;阿里面试官一句话问倒了他。实际上&#xff0c;不在BAT这样的大厂工作&#xff0c;是很难接触到支撑千亿级流量微服务架构项目的。但也正是这种难得&#xff0c;让各个大厂都抢着要这样的人才&#x…

docker-harbor私有仓库

一、Harbor概述 1、Harbor的概念 • Harbor是VMware公司开源的企业级Docker Registry项目&#xff0c;其目标是帮助用户迅速搭建一个企业级的Docker Registry服务 • Harbor以 Docker 公司开源的Registry 为基础&#xff0c;提供了图形管理UI、基于角色的访问控制(Role Base…

华为流程体系:IPD流程框架(限制版)

目录 前言 详细内容 专栏列表 CSDN学院课程地址 前言 今天主要来谈谈 IPD 体系的主体框架所涉及的一些相关内容。 其实关于 IPD 体系&#xff0c;我在之前的文章或课程中都有过不同程度的讲解。 但是&#xff0c;由于这个体系所涉及的面是非常广泛的。 这个时候就必须通…

sql总理论加实践

一、基础查询 DQL语法 SELECT 字段列表 1.设置别名 SELECT 字段1[别名],字段二[别名]......FROM 表名 WHERE 条件列表; 2.查询多个字段 SELECT 字段1,字段2,字段3......FROM 表名; SELECT * FROM 表名; 3.去除重复记录 DISTANT FROM 表明列表 WHERE 条件列表(条件…

利用etcd实现分布式锁

python etcd3模块的lock使用 观察lock的加解锁影响 在python中已经自带了分布式锁的实现方式&#xff0c;下面我们尝试一下加锁与解锁的流程 在运行该demo同时也对lock对应的key进行watch&#xff0c;观察其变化&#xff0c;注意python-etcd3在实现分布式锁的时候&#xff0…

【Python爬虫开发基础⑤】HTML概述与基本标签详解

专栏&#xff1a;python网络爬虫从基础到实战 欢迎订阅&#xff01;近期还会不断更新~ 往期推荐&#xff1a; 【Python爬虫开发基础①】Python基础&#xff08;变量及其命名规范&#xff09; 【Python爬虫开发基础②】Python基础&#xff08;正则表达式&#xff09; 【Python爬…

探秘华为交换机:端口类型全解析

在下列情况下&#xff0c;判断的一般方法是什么&#xff1f; 1.交换机某个端口下的用户丢包。 2.交换机下的所有用户都在丢失数据包。 3、用户反映网速缓慢 检查网络电缆&#xff0c;重做水晶头&#xff0c;检查用户的计算机网卡&#xff0c;并重新启动交换机。 这几种做法都能…

Flink 学习五 Flink 时间语义

Flink 学习五 Flink 时间语义 1.时间语义 在流式计算中.时间是一个影响计算结果非常重要的因素! (窗口函数,定时器等) Flink 可以根据不同的时间概念处理数据。 处理时间: process time System.currentTimeMillis()是指执行相应操作的机器系统时间&#xff08;也称为纪元时间…

Docker安全

一、Docker 容器与虚拟机的区别 1、隔离与共享 • 虚拟机通过添加 Hypervisor 层&#xff0c;虚拟出网卡、内存、CPU 等虚拟硬件&#xff0c;再在其上建立虚拟机&#xff0c;每个虚拟机都有自己的系统内核 • Docker容器则是通过隔离的方式&#xff0c;将文件系统、进程、设…

Redis入门 - Lua脚本

原文首更地址&#xff0c;阅读效果更佳&#xff01; Redis入门 - Lua脚本 | CoderMast编程桅杆https://www.codermast.com/database/redis/redis-scription.html Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL。 …

Spark01-Spark快速上手、运行模式、运行框架、核心概念

1 概述 Spark和Hadoop Hadoop HDFS(GFS:TheGoogleFileSystem)MapReduce总结&#xff1a;性能横向扩展变得容易&#xff0c;横向拓展:增加更多的计算节点来扩展系统的处理能力Hbase&#xff1a;分布式数据库 Spark Spark CoreSpark SQLSQL 方言&#xff08;HQL)Spark Streamin…

光伏电池局部遮阴下三种不同的工况对比MATLAB仿真模型

光伏电池局部遮阴下三种不同的工况对比MATLAB仿真模型及程序资源-CSDN文库https://download.csdn.net/download/weixin_56691527/87910311 模型简介&#xff1a; 建议使用MATLAB21b及以上版本打开&#xff01; 光伏阵列表面被局部遮挡时会产生热斑效应。为了防止太阳电池因热…