C++11 新特性 ---- 模板的优化

C++11 模板机制:
        ① 函数模板
        ② 类模板
    
    模板的使用:
        ① 范围:模板的声明或定义只能在全局或类范围进行,不可以在局部范围(如函数)
        ② 目的:为了能够编写与类型无关的代码

    函数模板:
        - 格式:
            template <类型形参表或成模板参数列表> //类型函数声明
            返回类型 函数名 (形参表) {
                函数体
            }
        - 注意:
            模板参数表:定义在类或函数定义中用到的类型或值
            类型形参表:可包含基本数据类型和类类型。类型形参需要加class/typename。
                       其中class和typename等价
            T-通用的数据类型,名称可替换,通常为大写
    
    类模板:
        - 格式:
            template <类型形参表> //类型参数声明
            class 类名 {
                类模板的代码
            }
            
            template <类型参数表> //定义在类模板之外的函数必须以关键字template开始
            返回类型 类名 类型名表::成员函数n(形参表)
            {
                成员函数定义体
            }

一、模板的优化

1.模板的右尖括号

/*
    1.模板的右尖括号
*/
#include <iostream>
#include <vector>
using namespace std;

// 类模板Base
template<typename T>
class Base {
public:
    // 遍历容器的操作函数traversal()
    void travelsal(T& t) {
        auto it = t.begin();
        for(;it!=t.end();++it) {
            cout<<*it<<" ";
        }
        cout << endl;
    }
};

int main() {
    vector<int> v{1,2,3,4,5,6,7,8,9};
    Base<vector<int>> b;
    b.travelsal(v);
    return 0;
}

 ① c++98标准下的编译 

g++ -std=c++98 4.模板的优化.cpp -o app

②  c++11标准下的编译 

g++ -std=c++11 4.模板的优化.cpp -o app

 2.默认模板参数

在C++98/03标准中
        ① 类模板可以有默认的模板参数
        ② 不支持函数的默认模板参数

① 在C++98/03标准中,类模板可以有默认的模板参数

#include <iostream>
#include <vector>
using namespace std;
// 2.默认模板参数
/*
    在C++98/03标准中
        ① 类模板可以有默认的模板参数
        ② 不支持函数的默认模板参数
*/
// ① 类模板可以有默认的模板参数
template<typename T=char,T t = 'a'>
class Test{
public:
    void printString() {
        cout<< "current value: " << t <<endl;
    }
};

int main() {
    Test<> t;
    t.printString();
    Test<char,'b'> t1;
    t1.printString();
    return 0;
}

总结: 对于类模板,哪怕所有参数都有默认参数,在使用时也必须在模板名后跟随<>来实例化

② 在C++98/03标准中,不支持函数的默认模板参数

#include <iostream>
#include <vector>
using namespace std;
// 2.默认模板参数
/*
    在C++98/03标准中
        ① 类模板可以有默认的模板参数
        ② 不支持函数的默认模板参数
*/
// ② 不支持函数的默认模板参数
template<typename T=int> // C++98/03不支持这种写法, C++11中支持这种写法
void print(T t) {
    cout<< "current value: " << t <<endl;
}

int main() {
    print(1314);
    return 0;
}

c++11支持对函数模板的默认参数

总结:当所有模板参数都有默认模板,函数模板的调用如同一个普通函数。

③ C++11 结合默认模板参数和模板参数自动推导

#include <iostream>
#include <string>
using namespace std;

template<typename R=int,typename N>
R test(N arg) {
    return arg;
}

int main() {
    auto ret1 = test(666);
    cout<<"return value-1: "<<ret1<<endl;//666

    auto ret2 = test<double>(54.321);
    cout<<"return value-2: "<<ret2<<endl;//54.321

    auto ret3 = test<int>(54.321);
    cout<<"return value-3: "<<ret3<<endl;//54

    auto ret4 = test<char,int>(100);
    cout<<"return value-4: "<<ret4<<endl;//d

    return 0;
}

④ C++11 同时使用默认模板参数和模板参数自动推导

#include <iostream>
#include <string>
using namespace std;

// 函数的定义
template<typename T,typename U = char>
void func(T arg1 = 99,U arg2 = 99) {
    cout<<"arg1: "<<arg1<<", arg2: "<<arg2<<endl;
}

int main() {
    // 模板函数调用
    func('a');
    func(97,'a');
    // func();//编译报错
    return 0;
}

 func();编译出错是必须确定出T的数据类型,才能使用。

 总结:

① 若可以推导出参数类型,就使用推导出的类型

② 若函数模板无法推导出参数类型,则编译器会使用默认模板参数

③ 若无法推导出模板参数 且 没有设置默认模板参数,编译器会报错

 概括:
 
① 模板参数类型的自动推导是根据模板函数调用时指定的实参进行推导的,无实参则无法推导 

② 模板参数类型的自动推导不会参考函数模板中指定的默认参数

⑥ 函数模板使用时,自动类型推导,必须推导出一直的数据类型T,才可以使用

#include<iostream>
using namespace std;
template<class T>
T add(T a,T b) {
    return a + b;
}

// 1.自动类型推导,必须推导出一致的数据类型T,才可以使用
int main() {
    int a=1;
    int b=2;
    char c='c';
    add(a,b);//正确,可以推导出一致T
    add(a,c);//错误,推导不出一致的T
}

3.类模板

类模板:
    类模板允许用户为类定义一种模式,使得类中的某些数据成员、成员函数的参数或成员函数
    的返回值能取任意类型。类模板的成员函数被认为是函数模板。

    template <类型形参表> //类型参数声明
    class 类名 {
        类模板的代码
    }
    
    template<类型形参表> //定义在类模板之外的函数必须以关键字template开始
    返回类型 类名 类型名表::成员函数n(形参表) {
        成员函数定义体
    }

① 实例化类模板

1.实例化类模板
        ① 类模板不能直接使用,必须先实例化为相应的模板类。
        ② 定义类模板之后,创建模板类:
            类模板名<类型实参表> 对象表;
            类型实参表与类模板中的类型形参表相匹配
#include <iostream>
using namespace std;
/*
    实例化类模板
        ① 类模板不能直接使用,必须先实例化为相应的模板类。
        ② 定义类模板之后,创建模板类:
            类模板名<类型实参表> 对象表;
            类型实参表与类模板中的类型形参表相匹配
*/ 
template<class T1,class T2>
class Student{
public:
    T1 name;//名字
    T2 age;//年龄
    Student(T1 k,T2 v) : name(k),age(v) {}; 
    // 重载操作符:operator ,对小于号< 进行重载
    bool operator < (const Student<T1,T2>& p) const;
};

template<class T1,class T2>
bool Student<T1,T2>::operator < (const Student<T1,T2>& p) const {
    // Pair的成员函数 operator< "小"的意思是关键字小
    return age < p.age;
}

int main() {
    Student<string,int> stu1("Sakura",8);//实例化出一个类Pair<string,int>
    cout <<"My name is " << stu1.name << ",and age is " << stu1.age <<endl;
    Student<string,int> stu2("Luffy",14);//实例化出一个类Pair<string,int>
    cout <<"My name is " << stu2.name << ",and age is " << stu2.age <<endl;
    bool isYoung = stu1.operator<(stu2);
    if(isYoung) {
        cout<<stu1.name<<" is younger than him!!!\n";
    }else {
        cout<<stu2.name<<" is younger than her!!!\n";
    }
    return 0;
}

 

// 2.类模板实例
#include <iostream>
#include <string>
#include <initializer_list>
using namespace std;

template <typename T>
class Array { //类模板
    int size;
    T* p;
public:
    Array();//默认构造函数
    Array(int n);//重载构造函数
    Array(initializer_list<T> lst);
    T& operator[](int) ;//[]重载函数
    int getSize();//获取数组大小
};

// 默认构造函数
template <typename T>
Array<T>::Array() {
    size = 10;
    p = new T[size];
}

// 重载构造函数
template<typename T>
Array<T>::Array(int n) {
    size = n;
    p = new T[size];
}

// 下标运算符重载函数
template<typename T>
T& Array<T>::operator[](int i) {
    if(i>=0 && i<size) {
        return p[i];
    }
}

template<typename T>
Array<T>::Array(initializer_list<T> lst){
    size = lst.end() - lst.begin();
    p = new T[size];
    //cout<<"size大小:"<<size<<endl;
    int k=0;
    for (auto it = lst.begin(); it != lst.end(); ++it)
	{
		//cout<<*it<<endl;
        p[k++] = *it;
    }
    
}

template<typename T>
int Array<T>::getSize(){
    cout<<"获取size:"<<size<<endl;
    return size;
}

int main() {
    Array<int> arr1(2);
    arr1[0] = 520;
    arr1[1] = 1314;
    Array<int> arr2({1,2,3,5});
    
    cout<<"==============打印arr1数组=============="<<endl;
    int size = arr1.getSize();
    for (int i = 0; i < size; i++) {
        cout<<arr1[i]<<" ";
    }
    cout<<endl;

    cout<<"==============打印arr2数组=============="<<endl;
    size = arr2.getSize();
    for (int i = 0; i < size; i++) {
        cout<<arr2[i]<<" ";
    }
    cout<<endl;

    int a=10;int b=20;int c=30;
    cout<<"==============打印arr3数组=============="<<endl;
    Array<int> arr3({a,b,c}); 
    size = arr3.getSize();
    for (int i = 0; i < size; i++) {
        cout<<arr3[i]<<" ";
    }
    cout<<endl;
    return 0;
}

学习initializer_list,可以看这篇博客
C++ 函数传递多参数处理 可变参数模板_c++ 可变参数传递_Mr.禾的博客-CSDN博客icon-default.png?t=N6B9https://blog.csdn.net/qq_24447809/article/details/114793958

#include <iostream>
#include <string.h>
#include <initializer_list>
using namespace std;

template <typename T>
class Array { //类模板
    int size;
    T* p;
public:
    Array();//默认构造函数
    Array(int n);//重载构造函数
    T& operator[](int) ;//[]重载函数
    int getSize();//获取数组大小
};

// 默认构造函数
template <typename T>
Array<T>::Array() {
    size = 10;
    p = new T[size];
}

// 重载构造函数
template<typename T>
Array<T>::Array(int n) {
    size = n;
    p = new T[size];
}

// 下标运算符重载函数
template<typename T>
T& Array<T>::operator[](int i) {
    if(i>=0 && i<size) {
        return p[i];
    }
}

template<typename T>
int Array<T>::getSize(){
    cout<<"获取size:"<<size<<endl;
    return size;
}

class Student {
    
public:
    int no;
    char name[10];
    Student() {}
    Student(int n,const char* s) {
        no = n;
        strcpy(name,s);//使用strcpy为字符串数组赋值
    }
    Student &operator=(Student& s) {//赋值重载构造函数
        no = s.no;
        strcpy(name,s.name);
        return *this;
    }
    void display() {
        cout<<"学号: " << no <<",姓名: "<<name<<endl;
    }
};

int main() {
	cout << "学生列表" << endl;
	Array<Student> arr(4);
    int size = arr.getSize();
	Student x(1, "Tom"), y(2, "Jerry"), z(3, "Sakura");
    Student other = x;

	arr[0] = x; arr[1] = y; arr[2] = z,arr[3] = other;
	for (int i=0;i < size;i++) {
		arr[i].display();
	}
    return 0;
}

 ② 类模板作为函数参数

#include <iostream>
using namespace std;
//类模板作为函数参数
template<class T> 
class A {
    T x;
public:
    A(T a) {
        x = a;
    }
    T abs() {
        if(x<0) return -x;
        else return x;
    }
};

//函数模板中模板类作为形参
template<class T>
void func(A<T> x) {
    cout<<x.abs()<<endl;
}
int main()
{
	//建立对象
	A<int> a(-5);
	A<double> b(-5.8);
	func(a);
	func(b);
 
}

 ③ 函数模板作为类模板的成员

类模板中的成员函数可以是一个函数模板

// 函数模板作为类模板的成员
#include<iostream>
using namespace std;
template<class T>
class A{
public:
    template<class T2>
    void print(T2 t) {cout<<t;}//成员函数模板
};

int main() {
    A<int> a;
    //成员函数模板func被实例化
    a.print('K');     //K
    cout<<endl;
    a.print("hello"); //hello
    cout<<endl;
    a.print(123);     //123
    cout<<endl;
    return 0;
}

④ 类模板中也可以使用非类型参数,即值参数

#include<iostream>
using namespace std;
template<class T,int size>
class A{
public:
    T sum(T a) {
        return a+size;
    }
};

int main(){
    A<char,3> a;
    cout<<a.sum('a')<<endl;// d
    return 0;
}

⑤ 类模板的友元函数

template<class T>
class A{
public:
    T value;
    A() {}
    A(T v):value(v){}
    friend void func1();//与参数类型无关的友元函数
    friend void func2(A<T>& a);//与参数类型有关的友元函数
};

template<class T>
void func1(){
    cout<<"func1"<<endl;
}

template<class T>
void func2(A<T>& a){
    cout<<"func2 value:" <<a.value<<endl;
}

int main() {
    A<double> a(1.2);
    func1<int>();//func1是模板类A<int>的友元函数
    func1<double>();//func1是模板类A<double>的友元函数
    func2<double>(a);//func2是模板类A<double>的友元函数
}

 4.可变参数模板

① 可变参数模板例子

    可变参数模板:
        ① C++11增强了模板功能,允许模板定义中包含0到任意个模板参数
        ② 对参数进行了高度泛化,能表示任意个数,任意类型的参数
        ③ 是一个接收可变数目参数的模板函数或模板类
        ④ 可变数目的参数被称为参数包
    
    语法:
        template<class... T>
        void f(T... args);
    
    声明一个参数包T... args,这个参数包中可以包含0到任意个模板参数
/*
    可变参数模板:
        ① C++11增强了模板功能,允许模板定义中包含0到任意个模板参数
        ② 对参数进行了高度泛化,能表示任意个数,任意类型的参数
        ③ 是一个接收可变数目参数的模板函数或模板类
        ④ 可变数目的参数被称为参数包
    
    语法:
        template<class... T>
        void f(T... args);
    
    声明一个参数包T... args,这个参数包中可以包含0到任意个模板参数
*/
#include <iostream>
#include <map>
using namespace std;
template<typename T,typename... Args>//func是可变参数函数模板 有一个名为T的类型参数和一个名为args的模板参数包
void func(const T&t,const Args&...rest){//func函数列表包含一个const& 类型的参数 指向T的类型,名为rest的函数参数包
    cout<<sizeof...(rest)<<endl;//输出函数参数的数目
};

int main(){
    int a=0;double d=3.14;string str="heheda";
    func(a,str,42,d);//包中有三个参数
	func(str, 42, d);//包中有两个参数
	func(d, str);//包中有一个参数
	func("hi");//空包
    return 0;
}

② 可变参数模板的展开

    可变参数模板的展开:通过展开参数包的方式来获取参数包中的每个参数
    两种展开方式:
        ① 通过递归函数来展开参数包
        ② 通过逗号表达式来展开参数包
/*
    可变参数模板的展开:通过展开参数包的方式来获取参数包中的每个参数
    两种展开方式:
        ① 通过递归函数来展开参数包
        ② 通过逗号表达式来展开参数包
*/
#include <iostream>
using namespace std;
//递归终止函数
void print()
{
   cout << "empty!!!" << endl;
}

//展开函数
template<class T,class... Args>
void print(T head,Args... rest) {
    cout<<"param:"<<head<<endl;
    print(rest...);
}

int main(void)
{
   print(1,2,3,4);
   return 0;
}

③ 可变模板参数求和

#include <iostream>
using namespace std;
template<typename T>
T sum(T t){
    return t;
}
template<typename T, typename ... Types>
T sum (T first, Types ... rest) {
    return first + sum<T>(rest...);
}
int main(void)
{
   cout<<sum(1,2,3,4)<<endl; //10
   return 0;
}

④ 逗号表达式展开参数包

#include <iostream>
using namespace std;

//逗号表达式展开参数包
template <class T>
void printArg(T t)
{
   cout << t << " ";
}
 
template <class ...Args>
void expand(Args... args)
{
   int arr[] = {(printArg(args), 0)...};
}
int main(){
    expand(1,2,3,4);
    cout<<endl;
    return 0;
}

 

  5.可变参数模板类的展开

//前向声明
template<typename... Args> struct Sum;
 
//基本定义
template<typename First, typename... Rest>
struct Sum<First, Rest...>
{
    enum { value = Sum<First>::value + Sum<Rest...>::value };
};
 
//递归终止
template<typename Last>
struct Sum<Last>
{
    enum { value = sizeof (Last) };
};

学习和参考一下文章:

模板的优化 | 爱编程的大丙 (subingwen.cn)icon-default.png?t=N6B9https://subingwen.cn/cpp/template/C++11 模板用法总结_herryone123的博客-CSDN博客icon-default.png?t=N6B9https://blog.csdn.net/kenjianqi1647/article/details/118207899

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

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

相关文章

软件工程:帕金森定律

在软件开发中&#xff0c;你是否遇到过这种情况&#xff1a; 团队要开发一个简单的购物车应用&#xff0c;项目预期时间是2周工期。负责开发的工程师默认利用完整的2周时间来完成任务。在第一周&#xff0c;工程师会认为任务很轻松&#xff0c;有充足的时间来完成任务&#xff…

SPM(Swift Package Manager)开发及常见事项

SPM怎么使用的不再赘述&#xff0c;其优点是Cocoapods这样的远古产物难以望其项背的&#xff0c;而且最重要的是可二进制化、对xcproj项目无侵入&#xff0c;除了网络之外简直就是为团队开发的项目库依赖最好的管理工具&#xff0c;是时候抛弃繁杂低下的cocoapods了。 一&…

Camunda 7.x 系列【2】开源工作流引擎框架

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Spring Boot 版本 2.7.9 本系列Camunda 版本 7.19.0 源码地址&#xff1a;https://gitee.com/pearl-organization/camunda-study-demo 文章目录 1. 前言2. 开源工作流引擎框架2.1 jBPM2.2 Activ…

Python识别抖音Tiktok、巨量引擎滑块验证码识别

由于最近比较忙&#xff0c;所以本周搞了一个相对简单的验证码&#xff0c;就是抖音Tiktok的滑块验证码&#xff0c;这也是接到客户的一个需求。这种验证码通常在电脑端登录抖音、巨量引擎的的时候出现。 首先看一下最终的效果&#xff1a; 验证码识别过程 1、利用爬虫采集图…

jenkins的cicd操作

cicd概念 持续集成&#xff08; Continuous Integration&#xff09; 持续频繁的&#xff08;每天多次&#xff09;将本地代码“集成”到主干分支&#xff0c;并保证主干分支可用 持续交付&#xff08;Continuous Delivery&#xff09; 是持续集成的下一步&#xff0c;持续…

【ArcGIS Pro二次开发】(57):地图系列

在ArcGIS Pro中&#xff0c;有一个地图系列&#xff0c;可以在一个布局中导出多个地图。 在SDK中为ArcGIS.Desktop.layout.MapSeries类和映射系列导出选项&#xff0c;可以以支持多页导出。 MapSeries类提供了一个静态CreateSpatialMapSeries方法&#xff0c;该方法使用指定的…

【0807作业】使用消息队列实现AB进程对话+使用共享内存实现A进程打印字符串,B进程逆置字符串,打印结果为【正序 逆序 正序 逆序】

作业一&#xff1a;使用消息队列实现AB进程对话 ① 打开两个终端&#xff0c;要求实现AB进程对话 A进程先发送一句话给B进程&#xff0c;B进程接收后打印B进程再回复一句话给A进程&#xff0c;A进程接收后打印重复1.2步骤&#xff0c;当收到quit后&#xff0c;要结束AB进程 ② …

K8s中的PV和PVC和监控

1.PV和PVC PV&#xff1a;持久化存储&#xff0c;对存储资源进行抽象&#xff0c;对外提供可以调用的地方&#xff08;类似&#xff1a;生产者&#xff09; PVC&#xff1a;用于调用&#xff0c;不需要关心内部实现细节&#xff08;类似&#xff1a;消费者&#xff09; 2.实…

ChatGPT 作为 Python 编程助手

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建可编辑的3D应用场景 简单的数据处理脚本 我认为一个好的起点是某种数据处理脚本。由于我打算让 ChatGPT 之后使用各种 Python 库编写一些机器学习脚本&#xff0c;这似乎是一个合理的起点。 目标 首先&#xff0c;我想尝试…

8.7一日总结

后台管理项目(使用vue3) 1.创建项目 npm init vuelatest 2.进入项目,下载依赖 3.下载需要的项目依赖 下载重置样式表 npm install reset-css 在main.js中阴入 import reset-css 4.清理目录 将项目中不需要的内容删除 5.运行项目 npm run dev 6.将仓库推送…

Unity Shader编辑器工具类ShaderUtil 常用函数和用法

Unity Shader编辑器工具类ShaderUtil 常用函数和用法 Unity的Shader编辑器工具类ShaderUtil提供了一系列函数&#xff0c;用于编译、导入和管理着色器。本文将介绍ShaderUtil类中的常用函数和用法。 编译和导入函数 CompileShader 函数签名&#xff1a;public static bool C…

一百四十三、Linux——Linux的CentOS 7系统语言由中文改成英文

一、目的 之前安装CentOS 7系统的时候把语言设置成中文&#xff0c;结果Linux文件夹命名出现中文乱码的问题&#xff0c;于是决定把Linux系统语言由中文改成英文 二、实施步骤 &#xff08;一&#xff09;到etc目录下&#xff0c;找到配置文件locale.conf # cd /etc/ # ls…

Vue3 第三节 计算属性,监视属性,生命周期

1.computed计算属性 2.watch监视函数 3.watchEffect函数 4.Vue的生命周期函数 一.computed计算属性 计算属性简写和完整写法 <template><h1>一个人的信息</h1>姓&#xff1a;<input type"text" v-model"person.firstName" />…

100G光模块的应用案例分析:电信、云计算和大数据领域

100G光模块是一种高速光模块&#xff0c;由于其高速率和低延迟的特性&#xff0c;在电信、云计算和大数据领域得到了广泛的应用。在本文中&#xff0c;我们将深入探讨100G光模块在这三个领域的应用案例。 一、电信领域 在电信领域&#xff0c;100G光模块被广泛用于构建高速通…

什么是应用的贴身防护盾?来看F5洞察风险

从云原生的发展来看&#xff0c;由于云原生技术的革新以及现代应用随之进化&#xff0c;可以清晰地看到云原生吞噬一切的势头&#xff0c;但是云原生也面临着众多挑战。2022 年很多公开调查报告显示&#xff0c;在云原生发展中&#xff0c;CEO/CIO 最关注的就是云原生安全&…

FFmpeg将编码后数据保存成mp4

以下测试代码实现的功能是&#xff1a;持续从内存块中获取原始数据&#xff0c;然后依次进行解码、编码、最后保存成mp4视频文件。 可保存成单个视频文件&#xff0c;也可指定每个视频文件的总帧数&#xff0c;保存多个视频文件。 为了便于查看和修改&#xff0c;这里将可独立的…

SpringBoot整合Sfl4j+logback的实践

一、概述 对于一个web项目来说&#xff0c;日志框架是必不可少的&#xff0c;日志的记录可以帮助我们在开发以及维护过程中快速的定位错误。slf4j,log4j,logback,JDK Logging等这些日志框架都是我们常见的日志框架&#xff0c;本文主要介绍这些常见的日志框架关系和SpringBoot…

自然语言处理: 第六章Transformer- 现代大模型的基石

理论基础 Transformer&#xff08;来自2017年google发表的Attention Is All You Need (arxiv.org) &#xff09;&#xff0c;接上面一篇attention之后&#xff0c;transformer是基于自注意力基础上引申出来的结构&#xff0c;其主要解决了seq2seq的两个问题: 考虑了原序列和目…

拦截器对接口细粒度权限校验

文章目录 一、逻辑分析二、校验规则1.规则类型2.规则划分3.规则配置信息4.规则案例说明5.规则加载 三、拦截器定义1.自定义拦截器2.注册拦截器 四、获取请求参数1.获取get提交方式参数2.获取post提交方式参数&#xff08;1&#xff09;定义RequestWrapper类&#xff08;2&#…