【c++模板笔记一】模板的介绍及其重载

2023-10-29

2015年2月11日 周三

        有一段时间没有更新博客了,这几天在整理前段时间所学的c++知识点就没有更新了。最近开始研究c++的模板的STL,于是开始继续写下自己的一点所得吧。

        模板和STL都是c++中比较实用的东西,能把我们省下很多事情(简化编码)。今天作为模板的第一讲,今天讲的模板其实主要是介绍函数模板。模板还有类模板(我们明天介绍)。

————————————————————分割线——————————————————————

一、为什么需要函数模板?

       在介绍什么是函数模板之前,我们还是先一起想想,到底为什么需要这个东西呢?刚好可以复习前面的知识点。
       现在我们一起来设计程序:用函数来求两个变量中的最大值(这两个变量,可能是int,double和const char*)。
       首先,我们用最传统的 c语言的 解决方法来写代码:
#include <stdio.h>
#include <string.h>
int max_int(int a,int b){
    return a > b ? a : b;
}
double max_double(double a,double b){
    return a > b ? a : b;
}
const char* max_char(const char* a,const char* b){
    return strcmp(a,b) > 0 ? a : b;
}
int main()
{
    printf("%d\n",max_int(100,200));
    printf("%f\n",max_double(1.2,2.2));
    printf("%s\n",max_char("hello","world"));
    return 0;
}
        因为变量的类型不同,我们必须设计不一样的函数去分别解决他们。
        一共有三种类型,就要去定义三个函数,如果有100种类型,就要去定义100个函数。这就是函数的一个缺点: 虽然能保证函数结果不会出一点错,但是必须为不同类型的进行不同的函数实现。
        是不是感觉有点麻烦啊?你说有没有什么简单的方法啊?(c语言中没有函数重载哦,重载我们下面再讲。)其实在之前c程序猿就开始想这个问题了——如果做到 类型无关
        于是,在c语言中就提出了一个东西—— 函数宏(带参宏)
        关于什么是函数宏我们这里不做过多的介绍,如果又不懂的同学请自觉向度娘学习。我们用函数宏的方法,解决上面的这个问题:
#include <stdio.h>
#include <string.h>
#define Max(a,b) ((a) > (b) ? (a) : (b))
int main()
{
    printf("%d\n",Max(100,200));
    printf("%f\n",Max(1.2,2.2));
    printf("%s\n",Max("hello","world"));
    return 0;
}

       函数宏的原理就是,在预编译时所有Max(a,b) 都会自动被替换为((a) > (b) ? (a) : (b))。所以我们可以看到答案基本正确。但是请仔细看第三个答案,为什么“hello”比“world”大!按照字符串比较原则, 明明“world”大啊!
       还记得我们在进行字符串比较的时候都是用strcmp()函数的吗?直接比较会有什么后果?直接比较的话,其实比较的是两个const char*变量,是在比较指针而不是指针所指向字符串的值!!!这就是函数宏的一个缺点: 虽然能适用于大多数参数类型, 不能保证所有对所有类型都安全
       看来在c中没有很好的解决这个问题啊!
       我们用c++来试试!其实我知道你憋了很久了,你肯定迫不及待的想使用 函数重载了!(如果你对于c++的函数重载还不是很懂,欢迎点开我的 【c++笔记二】重载(overload)之一看你就懂
下面我们就用函数重载写一写:
#include<iostream>
using namespace std;
int Max(int a,int b){
    return a>b?a:b;
}
double Max(double a,double b){
    return a>b?a:b;
}
string Max(string a,string b){
    return a>b?a:b;
}
int main()
{
    cout<<Max(100,200)<<endl;
    cout<<Max(1.2,2.2)<<endl;
    cout<<Max("hello","world")<<endl;
    return 0;
}

       在c++中我们用string类取代了c语言中的字符串数组(char*),直接使用“>”去比较两个字符串就更安全了。
       或许你可以发现,针对三个类型我们还是写了三个函数,但是和c语言比较起来至少函数名都一样了(不用费脑筋去想各种各样的函数名了)。而且你发现没有,函数的实现体都是一模一样的,这样的重载函数多了,你会发现代码有点 冗余
       所以,c++正式引入—— 模板
       终于扯到我们今天的重点上来了,模板在经历了我们说的:函数、宏函数、重载之后,综合了三者所长。你想,你做个视频,网上有各种视频模板素材,你只要套着模板自己修改一下就可以做出高大上的视频了。做PPT也可以用模板、做简历也有模板。生活中充斥着各种各样的模板,模板给我们带来的便利自然不用我多说吧?童同样的,用上了c++的模板之后,我们也可以省很多事呢!下面正式开始讲解模板。

二、模板的定义和使用

1.定义语法:

template<typename 类型形参1, typename 类型形参2, ...>
返回类型 函数模板名 (调用形参表) { 
       函数体; 
}
       首先你要用一个template(模板的英文名)关键字表明你要开始写模板啦!然后在“<>”里面,写上你typename(类型名字)+函数中要用到的形参名,这样你就不用指定具体的形参的参数类型啦,可以写不止一个哦!换一行我们继续来写具体的函数实现体。返回类型和形参类型都可以用"<>"里面定义的那些名字来代替哦!
       我们具体看看怎么做吧?把一开始写的那个代码,改成用模板来写!
template<typename T>
T Max(T a,T b){
    return a>b?a:b;
}
我们的函数体,就浓缩为这四行了。这就是模板的魅力之处——可通用所有的类型!这里你需要特别注意这个typename关键字定义的T,它代表的是任何合法的数据类型,无论是系统定义的(int、double之类的)或者是你自己定义的结构体或类类型都可以的!

2.模板的使用

        模板是定义出来 了,我们怎么去使用他们呢?肯定有自己独特的使用方法吧!
函数模板名<类型实参1, 类型实参2, ...> (调用实参表);
       使用函数模板其实也很简单,就比调用普通函数多了一个“<>”。这个"<>"里面需要你写上你具体想用哪种数据类型去 实例化模板!(原理下面再讲,先学会怎么去用)
       所以我们调用模板,把最开始写的代码运行起来!
#include<iostream>
using namespace std;
template<typename T>
T Max(T a,T b){
    return a>b?a:b;
}
int main()
{
    cout<<Max<int>(100,200)<<endl;
    cout<<Max<double>(1.2,2.2)<<endl;
    cout<<Max<string>("hello","world")<<endl;
    return 0;
}

Max<int>(100,200),我们是在告诉编译器,100和200是int类型的,你用int类型去使用模板就好了!函数模板中的T就会自动变成int类型啦。
       那到底编译器是如何实现这种功能的呢?我们在一起看看模板的实现方法。

3.模板的实现

模板是如何做到这样强大的功能的呢?很简单,就四个字: 二次编译
       1)第一次编译,是编译器针对函数模板的 定义所做的编译, 一般性的语法检查,生成关于该模板的内部表示。
       2)第二次编译,是编译器针对函数模板的 调用所做的编译,用所提供的类型实参结合其内部表示中的类型形参,做类型相关性检查,生成针对 具体函数的机器指令。
       第一次编译,也就是在静态时期,编译器只是检查一下你的函数体实现有没有什么语法错误,这时候不考虑数据的类型。
       第二次编译,是发生在运行时期,编译器会根据你提供的数据类型,去替换第一次编译时暂代的模板,实现具体的代码指令。
       特别注意一点, 函数的模板不是函数,只是一个模板!只有这个模板被具体的数据类型实例化之后,才是一个可以使用的函数!
       其实模板也就这么简单是吧!当然不是,还有很多需要注意的地方哦!继续往下看。

4.隐式推断

       你有没有想过,如果这句话:Max<int>(100,200)中的“<>”那部分你没有写会怎么样呢?我们一起试验一下。
#include<iostream>
using namespace std;
template<typename T>
T Max(T a,T b){
    return a>b?a:b;
}
int main()
{
    cout<<Max(100,200)<<endl;
    cout<<Max(1.2,2.2)<<endl;
    cout<<Max("hello","world")<<endl;
    return 0;
}


       我们在调用函数模板的时候,都把“<>”部分省略了。程序没有报错,运行起来了,不过结果和我们所想的有所偏差。输出的结果和用宏函数时一样,第三个输出结果出错了。
       其实这里发生了“ 隐式推断”。编译器自动(隐式)地去推断了实参的类型,并告诉模板怎么具体的去实例化。
       是不是真的这样呢?我们函数模板的实现体中检测一下参数类型不就可以证明到底编译器是不是真的做了隐式推断。这里我们需要用到typeid(不懂的还是度娘哦)。
#include <iostream>
#include <typeinfo>
using namespace std;
template<typename T>
T Max(T a,T b){
    cout<<typeid(T).name()<<": ";
    return a>b?a:b;
}
int main()
{
    cout<<Max(100,200)<<endl;
    cout<<Max(1.2,2.2)<<endl;
    cout<<Max("hello","world")<<endl;
    return 0;
}


       我们在函数实现体中,先输出一下T的具体类型。我们在调用Max(100,200),编译器隐式推断出,100和200的类型为i(int)。同理,编译器也知道1.2和2.2类型为d(double)。我们注意,编译器认为“hello”和“world”的类型是PKc(const char*),所以他们实际上是在比较指针啊亲!(编译器可没那么聪明,知道把他们堪为string对象去比较)所以为什么比较结果显示“hello”大了(可能“hello”指针的值,就是指针的地址更大)。
       隐式推断的定义: 如果函数模板的调用参数(圆括号中的参数)的类型模板的模板参数(尖括号中的参数) 相关 ,那么在调用该函数模板时,即使不显式指定模板实参,编译器也有能力根据调用参数的类型推断出正确的模板实参,以获得与普通函数调用一致的语法表达。
       既然有隐式推断了,我们以后就不写模板参数了吧!这肯定不行,有 三种情况,是不能用隐式推断的:

A.模板参数与调用参数的类型无关

       我们看例子:
       调用参数是T类型的,而模板参数中还有一个R。因为R和函数参数 无关,编译器根本不能隐式推断出R的具体类型,所以编译器报错了,编译器最后一行说了,不能推断出模板参数R!

B.隐式推断不允许隐式类型转换

       我们先来看一个普通的代码:
#include <iostream>
using namespace std;
void show(i a){
    cout<<a<<endl;
}
int main()
{
    char ch='a';
    show(ch);
    return 0;
}
       show函数是用来输入int类型的形参a的,但是我们在主函数中调用show函数的时候,传入的实参却是一个char类型的变量ch。但是程序还是运行起来了,并且正确的输出了字母a 的ASCII码。这里,发生了 隐式类型转换,将char隐式的转换为了int。
       那么隐式推断能不能也能隐式类型转换呢?
       我们同时传入了一个int和char参数。但是模板中要求的两个参数要相同,这里不能发生隐式类型转换了,所以不能隐式推断!
       解决这种问题方法其实也很简单:强制类型转换和显示给定模板参数。如下:
#include <iostream>
using namespace std;
template<typename T>
void show(T a,T b){
    cout<<a<<" "<<b<<endl;
}
int main()
{
    show(10,(int)'a');
    show<int>(10,(int)'a');
    return 0;
}


C.返回类型不能隐式推断。

       我们还是直接看代码:
        我们调用模板add时候,只显示的给定了T参数的类型为int,编译器并不知道返回值的类型,所以记住,返回类型不能隐式推断。
 

三、函数模板的重载

1.定义:

同一作用域中,函数名相同,参数表不同的  函数模板和函数模板  或者  函数模板和普通函数  之间构成重载关系。  
       是不是和函数的重载的定义有几分类似?!模板的重载不仅可以发生在模板之间,还能发生在普通函数和模板之间,好神奇哦!
template<typename T>
T Max(T a,T b){
    return a > b ? a : b;
}
const char* Max(const char& a,const char* b){
    return strcmp(a,b) > 0 ? a : b;
}
template<typename T>
T Max(T a,T b,T c){
    return Max(Max(a,b),c);
}
       第一和第三个是函数模板,第二个是普通函数。我们可以发现三者的函数名皆相同,他们构成了重载关系。
       第一个模板函数和第二个普通函数,参数表不同所以是重载关系。
       第一个模板函数和第三个模板函数,模板参数个数不同所以也是重载关系。
       那么我问你一个问题哦:如果我们调用Max("hello","world"),你说编译器会调用第几个函数?这就牵涉到了模板重载的问题了。我们一起往下看重载的一些原则:

2.重载的规则:  

(1)编译器优先选择普通函数,除非函数模板能够产生更好匹配性的函数。
#include <iostream>
#include <cstring>
using namespace std;
template<typename T>
T Max(T a,T b){
    cout<<"T Max(T,T): ";
    return a > b ? a : b;
}
const char* Max(const char* a,const char* b){
    cout<<"const char* Max(const char*,const char*): ";
    return strcmp(a,b) > 0 ? a : b;
}
int main()
{
    cout<<Max("hello","world")<<endl;
    cout<<Max(100,200)<<endl;
    return 0;
}
       我们发现,我们调用Max("hello","world")的时候调用的却是普通函数而不是模板,按道理模板跟它也能匹配上。这就是模板重载的一大原则,优先选择普通函数。因为编译器觉得普通函数的 针对性更强一些。
       但是我们调用Max(100,200)的时候却是调用的模板,因为这时候模板的匹配性明显更好一点。这就是我们说的第一条原则。
       同时通过这种重载的方法,我们还很好的保证了如果比较的是两const char *时,函数实现体的正确性。
(2)在参数传递的过程中如果需要隐式类型转换,编译器将选择普通函数
#include <iostream>
using namespace std;
template<typename T>
T Max(T a, T b){
    cout<<"T Max(T,T): ";
    return a > b ? a : b;
}
int Max(int a,double b){
    cout<<"int Max(int,double): ";
    return a > b ? a : b;
}
int main()
{
    cout<<Max(1.23,(double)4)<<endl;
    cout<<Max(1.23,4)<<endl;
    return 0;
}

        在上面我们说隐式推断的时候,提到了:隐式推断不允许隐式类型转换。如这个程序一样,一旦发生了隐式类型转换,编译器会调用普通函数,而非模板函数。
(3)通过模板参数列表告知编译器使用函数模板时,针对指针的版本比针对任意类型的版本更具有针对性
 
       大家如果仔细观察的话, 前两条重载规则都是发生在不显示指定模板参数的情况下,模板自己做了隐式推断。
       如果我们现在,显示的指定模板参数,那么情况又有一点不同了。如果我们实参是两个 指针类型,那么: 针对指针的版本比针对任意类型的版本更具有针对性!
       我们一起看代码:
#include <iostream>
using namespace std;
template<typename T>
T Max(T a, T b){
    cout<<"T Max(T,T): ";
    return a > b ? a : b;
}
template<typename T>
T* Max(T* a, T* b){
    cout<<"T* Max(T*,T*): ";
    return a > b ? a : b;
}
const char* Max(const char* a, const char* b){
    cout<<"const char* Max(const char*,const char*): ";
    return a > b ? a : b;
}
int main()
{
    const char* ch1 = "hello";
    const char* ch2 = "world";
    cout<<Max<>(ch1,ch2)<<endl;
    return 0;
}
       这里有两个函数模板,一个普通函数构成重载。如果我们调用调用Max<>(ch1,ch2)你说编译器会调用哪个函数?
       你可能会说,这一看就调用第三个普通函数吗!两个都是const char*类型的,明显普通函数的针对性更强一些(我们在原则1中说的)!可是那时候我们调用的时候可没有"<>"这部分哦,那时候是编译器自己在隐式推断。现在我们现实调用模板参数列表"<>",编译器就一定会调用函数模板而不是普通函数了。我们一起来看看结果如何?
       果然,编译器不去调用普通函数了。因为实参类型是指针类型的,而我们刚好有没有指定模板参数具体是什么(我们写的是的空的‘<>“),这里编译器又开始隐式推断了。它推断出这是两个const char*类型的实参,是两个指针,那么编译器就会觉得针对指针版的模板比普通模板 针对性更强,就会调用针对指针版的模板。
       那我再问你一个问题,如果我们调用Max<>(&ch1,&ch2),你说编译器会调用哪个?ch1是一个一级指针,再对其&取地址,那就是二级指针啦!或许你会说,我们没有重载二级指针的模板啊!但是:N级指针不就是N-1级指针的一级指针吗?看看编译器到底用谁吧!
#include <iostream>
using namespace std;
template<typename T>
T Max(T a, T b){
    cout<<"T Max(T,T): ";
    return a > b ? a : b;
}
template<typename T>
T* Max(T* a, T* b){
    cout<<"T* Max(T*,T*): ";
    return a > b ? a : b;
}
const char* Max(const char* a, const char* b){
    cout<<"const char* Max(const char*,const char*): ";
    return a > b ? a : b;
}
int main()
{
    const char* ch1 = "hello";
    const char* ch2 = "world";
    cout<<Max<>(&ch1,&ch2)<<endl;
    return 0;
}
果不其然,编译器还是调用了针对指针版本的模板。
(4)显式指定的模板实参必须在所选择的重载版本中与调用参数的类型保持一致
你思考一下啊,上面(第三点原则)时我们调用Max<>(ch1,ch2)的时候没有写上具体的模板参数类型,编译器帮我们隐式推断了。如果我们这样写:Max<const char *>(ch1,ch2),那你说,编译器会调用第几个?
#include <iostream>
using namespace std;
template<typename T>
T Max(T a, T b){
    cout<<"T Max(T,T): ";
    return a > b ? a : b;
}
template<typename T>
T* Max(T* a, T* b){
    cout<<"T* Max(T*,T*): ";
    return a > b ? a : b;
}
const char* Max(const char* a, const char* b){
    cout<<"const char* Max(const char*,const char*): ";
    return a > b ? a : b;
}
int main()
{
    const char* ch1 = "hello";
    const char* ch2 = "world";
    cout<<Max<const char*>(ch1,ch2)<<endl;
    return 0;
}

       奇怪,显示的指定模板参数类型是const char*之后,它居然不调用针对指针版本的了!按道理,指针版本的更适合调用函数的指针类型啊?
       其实是这样的,我们显示的指定模板参数是const char* 之后,T就等价于const char* ,那你说T*等价于什么?当然是const char* *注意,这时候T*就是一个二级指针。 而我们的调用函数是一级指针,当然不太匹配啊。所以显示指定模板参数的时候,编译器会选择类型和调用参数保持一致的重载版本。
(5)在函数模板的实例化函数中,编译器仍然优先选择普通函数,前提是该函数必须在函数模板被第一次编译时可见。
我们先来一起分析代码:
#include <iostream>
#include <cstring>
using namespace std;
template<typename T>
T Max(T a, T b){
    cout<<"T Max(T,T): ";
    return a > b ? a : b;
}
const char* Max(const char* a, const char* b){
    cout<<"const char* Max(const char*,const char*)"<<endl;
    return strcmp(a,b)>0?a:b;
}
template<typename T>
T Max(T a,T b,T c){
    cout<<"T Max(T,T,T)"<<endl;
    return Max(Max(a,b),c);
}
int main()
{
    const char* ch1 = "123";
    const char* ch2 = "12";
    const char* ch3 = "1";
    cout<<Max<const char*>(ch1,ch2,ch3)<<endl;
    return 0;
}

       这次我们不再是比较两个变量的最大值了,而是三个,所以我们调用Max<const char*>(ch1,ch2,ch3)的时候肯定会调用含有三个变量的函数模板。所以我们在第一行看见输出了T Max(T,T,T)。可是程序没有我们想的那么简答,而是继续输出了两行。为什么呢?
       这和我们T Max(T,T,T)这个函数模板的实现有关。你可以看见,这个函数模板返回的是:Max(Max(a,b),c)。返回的时候还调用了Max含有两个参数的版本,还是递归调用。所以再输出两行也没什么好奇怪的。
       但是你发现没有,我们在主函数中可是显示的指定了模板参数为const char*类型的哦,都现实调用模板参数了为什么最后两次还是调用的普通函数?
       是,我们是显示指定模板参数了没错,但是这个const char*只是传到了T Max(T,T,T)这个函数模板的T中,但是我们返回的Max(Max(a,b),c)可没有显示指定模板参数,当然这里就不一定会调用模板函数啦!从调用Max(Max(a,b),c)开始,又和上面提到的规则一样了(普通函数的针对性更强)。
       这里牵涉到我们前面说的模板的实现的知识。二次编译中,当我们给T Max(T,T,T)这个模板给定const char*实例化为函数的过程中,编译器还要去实例化Max(Max(a,b),c)这两个函数模板。这过程中还是会优先调用普通函数。
       但是,如果我们把const char* Max(const char*,const char*)的位置放到T Max(T,T,T)这个函数模板之后,输出结果会还是这样吗?
#include <iostream>
#include <cstring>
using namespace std;
template<typename T>
T Max(T a, T b){
    cout<<"T Max(T,T)"<<endl;
    return a > b ? a : b;
}
template<typename T>
T Max(T a,T b,T c){
    cout<<"T Max(T,T,T)"<<endl;
    return Max(Max(a,b),c);
}
const char* Max(const char* a, const char* b){
    cout<<"const char* Max(const char*,const char*)"<<endl;
    return strcmp(a,b)>0?a:b;
}
int main()
{
    const char* ch1 = "123";
    const char* ch2 = "12";
    const char* ch3 = "1";
    cout<<Max<const char*>(ch1,ch2,ch3)<<endl;
    return 0;
}

       你会发现,它不再去调用普通函数了。这是因为,普通函数在该函数模板之后,在第一次编译时,函数模板发现不了这个普通函数(无法完成静态的关联)。
       所以以后再模板中嵌套模板的时候, 大家要多多注意。
       综述, 在函数模板的实例化函数中,编译器仍然优先选择普通函数,前提是该函数必须在函数模板被第一次编译时可见
———————————————————————————分割线——————————————————————————
       好了,今天的知识点就讲到这里了。
       我们一起来稍微总结一下:首先我们介绍了为什么要引入模板。接着我们介绍了怎么去定义和使用模板,并且分析了模板的实现方法和一些隐式推断规则。最后我们还一起推断了一下模板重载的一些规则。
       如果大家对本文有什么疑问,或者有什么不足之处,欢迎评论留言。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【c++模板笔记一】模板的介绍及其重载 的相关文章

  • 使用 gcc 在 Linux 上运行线程构建块 (Intel TBB)

    我正在尝试为线程构建块构建一些测试 不幸的是 我无法配置 tbb 库 链接器找不到库 tbb 我尝试在 bin 目录中运行脚本 但这没有帮助 我什至尝试将库文件移动到 usr local lib 但这又失败了 任何的意见都将会有帮助 确定您
  • 调用 McAfee 病毒扫描引擎

    我收到客户的请求 要求使用他们服务器上的 McAfee 病毒扫描将病毒扫描集成到应用程序中 我做了一些调查 发现 McScan32 dll 是主要的扫描引擎 它导出各种看起来有用的函数 我还发现提到了 McAfee Scan Engine
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • C++11 删除重写方法

    Preface 这是一个关于最佳实践的问题 涉及 C 11 中引入的删除运算符的新含义 当应用于覆盖继承父类的虚拟方法的子类时 背景 根据标准 引用的第一个用例是明确禁止调用某些类型的函数 否则转换将是隐式的 例如最新版本第 8 4 3 节
  • std::vector 与 std::stack

    有什么区别std vector and std stack 显然 向量可以删除集合中的项目 尽管比列表慢得多 而堆栈被构建为仅后进先出的集合 然而 堆栈对于最终物品操作是否更快 它是链表还是动态重新分配的数组 我找不到关于堆栈的太多信息 但
  • 随着时间的推移,添加到 List 变得非常慢

    我正在解析一个大约有 1000 行的 html 表 我从一个字符串中添加 10 个字符串 td 每行到一个list td
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • 如何连接重叠的圆圈?

    我想在视觉上连接两个重叠的圆圈 以便 becomes 我已经有部分圆的方法 但现在我需要知道每个圆的重叠角度有多大 但我不知道该怎么做 有人有主意吗 Phi ArcTan Sqrt 4 R 2 d 2 d HTH Edit 对于两个不同的半
  • 如何在 C++ 中标记字符串?

    Java有一个方便的分割方法 String str The quick brown fox String results str split 在 C 中是否有一种简单的方法可以做到这一点 The 增强分词器 http www boost o
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • C# - 当代表执行异步任务时,我仍然需要 System.Threading 吗?

    由于我可以使用委托执行异步操作 我怀疑在我的应用程序中使用 System Threading 的机会很小 是否存在我无法避免 System Threading 的基本情况 只是我正处于学习阶段 例子 class Program public
  • LINQ:使用 INNER JOIN、Group 和 SUM

    我正在尝试使用 LINQ 执行以下 SQL 最接近的是执行交叉联接和总和计算 我知道必须有更好的方法来编写它 所以我向堆栈团队寻求帮助 SELECT T1 Column1 T1 Column2 SUM T3 Column1 AS Amoun
  • 如何在当前 Visual Studio 主机内的 Visual Studio 扩展中调试使用 Roslyn 编译的代码?

    我有一个 Visual Studio 扩展 它使用 Roslyn 获取当前打开的解决方案中的项目 编译它并从中运行方法 程序员可以修改该项目 我已从当前 VisualStudioWorkspace 成功编译了 Visual Studio 扩
  • 复制目录下所有文件

    如何将一个目录中的所有内容复制到另一个目录而不循环遍历每个文件 你不能 两者都不Directory http msdn microsoft com en us library system io directory aspx nor Dir
  • 有没有办法让 doxygen 自动处理未记录的 C 代码?

    通常它会忽略未记录的 C 文件 但我想测试 Callgraph 功能 例如 您知道在不更改 C 文件的情况下解决此问题的方法吗 设置变量EXTRACT ALL YES在你的 Doxyfile 中
  • 相当于Linux中的导入库

    在 Windows C 中 当您想要链接 DLL 时 您必须提供导入库 但是在 GNU 构建系统中 当您想要链接 so 文件 相当于 dll 时 您就不需要链接 为什么是这样 是否有等效的 Windows 导入库 注意 我不会谈论在 Win
  • C++ 继承的内存布局

    如果我有两个类 一个类继承另一个类 并且子类仅包含函数 那么这两个类的内存布局是否相同 e g class Base int a b c class Derived public Base only functions 我读过编译器无法对数
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置
  • 如何确定 CultureInfo 实例是否支持拉丁字符

    是否可以确定是否CultureInfo http msdn microsoft com en us library system globalization cultureinfo aspx我正在使用的实例是否基于拉丁字符集 我相信你可以使

随机推荐

  • c++ functor用处!!!

    某些特殊场景 函数中包含一些要赋的值 但是函数只能传入一个参数 例如使用count if 来得到长度大于len的字符的个数 class ShorterThan public explicit ShorterThan int maxLengt
  • Cadence 17.2 Padstack Editor入门指南(2)

    Cadence 17 2 Pad Editor入门指南 2 创建自定义焊盘及封装 Pad Editor与Allegro PCB Designer相互配合 可以做出各种类型的封装 当我们需要制作相对简单的封装时 可以用Pad Editor图形
  • faiss简介及示例

    faiss简介及示例 原文 https blog csdn net kanbuqinghuanyizhang article details 80774609 版权声明 本文为博主原创文章 未经博主允许不得转载 https blog csd
  • LLC和MAC子层的应用

    计算机局域网标准IEEE802 由于局域网只是一个计算机通信网 而且局域网不存在路由选择问题 因此它不需要网络层 而只有最低的两个层次 然而局域网的种类繁多 其媒体接入控制的方法也各不相同 为了使局域网中的数据链路层不致过于复杂 就应当将局
  • 多线程:线程内局部变量

    ThreadLocal类 该类提供了线程内局部 thread local 变量 好比有两个窗口 两个线程 一个窗口可以拿饮料 一个窗口可以拿食物 现在有多个人要来拿东西 如果在饮料窗口有的人拿到了饮料 有的人拿到了不该拿的食物 就说明线程之
  • 【性能测试】第一篇 性能测试概述

    性能测试的介绍 性能测试 通常意义上都是说的服务器性能 性能 内存 cpu 电量 流量 流畅度 为什么要进行性能测试 1 业务需求 例 电商双11活动 微信春晚抢红包 12306春运订票 当前服务器配置是否支持20000人同时使用 技术选型
  • 数据标注对新零售的意义及人工智能在新零售领域的应用?

    数据标签对于新零售至关重要 因为它构成了训练和部署人工智能 AI 和机器学习 ML 模型的基础 在新零售的背景下 数据标签涉及对数据进行分类 标记或注释以使其能够被机器理解的过程 然后 这些标记数据用于训练人工智能算法来识别模式 做出预测并
  • 深入浅出!十三张图带你从源码了解SpringBoot启动流程!实战篇

    前言 一位小伙伴准备了许久的阿里Java面试 原以为能够顺利拿下offer 但在第三面还是被摁在地上反复摩擦 丧气一段时间后 小伙伴调整了心态重新尝试了一下 最终拿下了offer 今天小编把这位小伙伴遇到的面试题分享出来 希望能对即将面试的
  • 元强化学习系列(1)之:元学习入门基础

    元强化学习三境界 统计学是人工智能开始发展的一个基础 古老的人们从大量的数据中发现七所存在的规律 在以统计学为基础的 机器学习 machine learning 时代 复杂一点的分类问题效果就不好了 深度学习 Deep Learning 的
  • 软件测试进阶之自动化测试

    扼要 1 了解软件自动化测试的概念 什么情况下进行软件自动化测试 2 了解软件自动化测试的分类 3 了解自动化测试的好处与确定 软件自动化测试 顾名思义就是使用软件对需要测试的软件进行机械地执行测试 自动化测试分类 自动化测试 通常分UI自
  • SPSS数据拆分(分组)

    本文中以CPI 消费者价格指数 分析为例 数据中分别给出了全国31个省市 在12个月 2018年8月 2019年7月 中的CPI 数据由 中国统计局 官网导出 引入 当我们从Excel文件导入数据后 如下图左 当我们分析的时候 我们希望得到
  • echarts 自定义鼠标悬停展示

    tooltip trigger axis formatter function params let htmlStr for let i 0 i lt params length i const param params i const x
  • 100天精通Python(可视化篇)——第82天:matplotlib绘制不同种类炫酷散点图参数说明+代码实战(二维散点图、三维散点图、散点图矩阵)

    文章目录 专栏导读 0 前言 1 参数说明 2 两主特征 二维散点图 1 普通散点图 2 文字标签散点图 3 带颜色映射的散点图 4 ArcGIS散点图 5
  • 微信小程序总结(2)- 需求分析

    在真正进入代码开发之前 很重要的一步就是进行需求分析 用户画像 这款微信小程序的主要用户是谁 是年轻人 中年人 还是老年人 是男生 还是女生 是工薪阶层 还是企业主 是金融理财 还是在线票务 在进行一定范围的样本调查后 可以得出一个精准的用
  • [Unity存档系统]简单介绍Unity常见存档系统之一PlayerPrefs以及使用方法

    学习目标 如果你和我同样苦恼于游戏相关的数据怎么存储与读取 那么不妨看看这个up主有关Unity存档系统的教程 Unity 存档系统 Part 1 PlayerPrefs Unity初学者系列教程 数据存取 存档读档 Save Load 哔
  • 实战:第六章:H5微信与支付宝调试错误,请回到请求来源地,重新发起请求。 错误代码 insufficient-isv-permissions

    H5支付宝支付 接人H5手机网站支付宝支付时 已经将表单发给页面了 支付宝响应调试错误 请回到请求来源地 重新发起请求 错误代码 insufficient isv permissions 错误原因 ISV权限不足 建议在开发者中心检查对应功
  • 【软件推荐系列第 3 篇】如何下载、设置时钟屏保

    这是 软件推荐系列第 3 篇 如果觉得有用的话 欢迎关注专栏 为避免 CSDN 审核时把这篇文章以广告类型划分 下面的字母 A 代表 fliqlo 后台不少人问我怎么给电脑设置时钟屏保的 效果如下图所示 其实很简单 下载一个叫 A 的软件就
  • 两数之和

    题目 给定一个整数数组 nums 和一个整数目标值 target 请你在该数组中找出 和为目标值 target 的那 两个 整数 并返回它们的数组下标 你可以假设每种输入只会对应一个答案 但是 数组中同一个元素在答案里不能重复出现 你可以按
  • LVGL V8下png图片缩放显示

    这几天在研究LVGL V8下显示png图片和缩放问题 1 软件硬件环境 硬件环境 宸芯科技的SS202X系列芯片 这里使用的是SS202D 软件环境 Linux 移植的嵌入式系统 LVGL V8 编译器 arm linux gnueabih
  • 【c++模板笔记一】模板的介绍及其重载

    2015年2月11日 周三晴 有一段时间没有更新博客了 这几天在整理前段时间所学的c 知识点就没有更新了 最近开始研究c 的模板的STL 于是开始继续写下自己的一点所得吧 模板和STL都是c 中比较实用的东西 能把我们省下很多事情 简化编码