C/C++语言基础--C++模板与元编程系列三(变量模板、constexpr、萃取等…………)

news2024/11/6 12:46:30

本专栏目的

  • 更新C/C++的基础语法,包括C++的一些新特性

前言

  • 模板与元编程是C++的重要特点,也是难点,本人预计将会更新10期左右进行讲解,这是第三期,讲变量模板、constexpr、萃取等知识
  • C语言后面也会继续更新知识点,如内联汇编;
  • 欢迎收藏 + 关注,本人将会持续更新。

文章目录

    • 非类型模板参数
    • 别名模板
    • 变量模板(C++14)
      • 定义格式
      • cosntexpr
        • constexpr定义的一定是常量
        • constexpr与函数
        • constexpr修饰接收函数返回值变量
      • 变量模板的使用
        • 非静态变量
        • 类静态变量
      • 在C++14之前没有变量模板之前
    • 类型萃取(简单了解)
      • is_pod

前言
上两次笔记咱们已经学习了C/C++语言基础–C++模板与元编程系列一(泛型、模板、函数模板、全特化函数模板………)和C/C++语言基础–C++模板与元编程系列二类模板、全特化、偏特化、编译模型简介、实现简单Vetctor等…………),加下来学习另外两个模板。

  • 别名模板(C++11):
  • 变量模板(C++14):

非类型模板参数

📘 准备

模板参数并不局限于定义类型,可以使用编译器内置类型作为参数,在编译期间变成模板的特定常量,这也是元编程的思想之一,C++元编程中核心思想之一就是在编译的时候确定类型,从而对不同类型进行不同计算

非类型模板参数:就是说在template定义模板类型的时候,可以定义编译器内置类型,这样就不是定义模板类型参数了。

👓 示例:

编写一个封装了静态数组的类,类名为Array

#include <iostream>

// 简单版本,不扩容
template<typename T, size_t _size=10>
class Array
{
public:
	void push_back(T data)
	{
		_array[_index++] = data;
	}

	T& operator[](size_t index)
	{
		return _array[index];
	}

	size_t size()const
	{
		return _index;
	}

private:
	T _array[_size]{};
	size_t _index{};
};

int main()
{
	Array<int> array;

	for (int i = 1; i <= 10; i++) {
		array.push_back(i);
	}

	for (int i = 0; i < array.size(); i++) {
		std::cout << array[i] << " ";
	}

	/*
	输出:1 2 3 4 5 6 7 8 9 10
	*/

	return 0;
}

☀️ 在案例中

  • 我们定义了一个模板类型参数T,同时也定义了一个非模板类型参数size_t类型,并且赋值为10;
  • 定义的非模板类型参数也是一样可以在类中使用,有点像函数的默认参数。

别名模板

我们在写程序的过程中,总是希望写简短的代码,给一些复杂的名称简化,或者取一个简短的名字,于是又有了类型别名,用来重新定义复杂的名字

🐤 C的typedef

使用typedef不仅可以对遍历取别名,还可以为模板具体化指定别名,🍡 🍡🍡 ​, 注意:是模板具体化

//模板别名
//1,typedef
typedef Array<int, 20> IntArr;

注意: 不能为模板类取别名。

// 如:
template <typename T>
typedef Array<T, 20> _Array;

报错:

在这里插入图片描述

解决方法:C++新特性using

⛅️ C++新特性using

typdef定义不了别名模板,所以C++11新增了一项功能——使用模板提供一系列别名(模板别名),当然也同样可以对编译器类型取别名。

🔛 模板具体类取别名

//2,using
using _IntArr = Array<int, 20>;  // 语法

🎊 模板类取别名

template <typename T>
using _Array = Array<T, 20>;

这样定义好之后,使用_Array就相当于使用Array<T,20>

变量模板(C++14)

概念:变量模板(Variable Templates)是 C++14 引入的一个新特性,它允许你定义一个模板,该模板在实例化时会生成一个具有特定类型的变量。变量模板的主要作用是减少代码冗余,提高代码的灵活性和可维护性。

概括

  • 模板实例化生成具体类型变量
  • 减少代码冗余

定义格式

template<typename T>
T name = value;
  • name:变量名
  • value:初始值

cosntexpr

变量模板一般会结合constexpr使用,constexprconst的升级优化版,最大区别是const定义的不一定是常量,但是constexpr一定是常量,这个什么意思呢??

如下代码:

int sum = 10;
const int csum = sum;

这里,将sum值赋值给const变量csum,但是csum是一个变量,不是常量啊,他在程序运行的时候才会是常量。


constexpr定义的一定是常量

C++针对以上情况,提出了cosntexpr这个关键字,这个关键字定义的变量是在编译的时候计算出来的,一定是常量,不可能是变量,如下代码:

int sum = 10;
constexpr int csum = sum;

报错error C2131: 表达式的计算结果不是常数


constexpr与函数

由于constexpr 是在编译的时候检查的,所以把函数声明为constexpr 了,这样编译器就会大胆优化,只要是出现CharMax()的地方,直接用127替代,是不是和宏很类似?确实,但是宏没有运行时安全检查,这也是为什么有些大佬提议用C++重写Linux内核的原因之一。

注意 : constexpr声明的函数,函数体不要写的太复杂(以前只允许一行代码)

如下:

constexpr uint8_t CharMax()
{
	int max = 117;
	for (int i = 0; i < 10; i++)
	{
		++max;
	}
	return max;
}

这个时候,调用constexpr 返回的就是127,常量的127


constexpr修饰接收函数返回值变量

废话不多说,直接上代码:

size_t getNum()
{
	return 127;
}
// 调用
constexpr size_t num = getNum();

// 报错:表达式必须含有常量值无法调用非 constexpr 函数"getNum"(已声明 所在行数:31)

☀️ 总结

  • 如果用constexpr修饰接收函数返回值变量,那么这个函数返回值夜泊徐要用constexpr修饰。

  • 解决方法如下:

  • constexpr size_t getNum()
    {
    	return 127;
    }
    

变量模板的使用

非静态变量
  • 从变量模板实例化的变量被称为**被实例化变量,从静态数据成员模板实例化的变量被称为被实例化静态数据成员。**

  • 一般结合constexpr使用,如下代码:

template<typename T>
constexpr T PI = T(3.14159265358L);

template<typename T>
T circle_area(T r)
{
	return PI<T> *r * r;	//PI<是变量模板实例化>
}

首先声明了一个变量模板PI,在后面就可以使用各种类型的PI了(如:PI、PI、PI等等),非常方便,简化了代码。


类静态变量

在类作用域中使用时,变量模板声明一个静态数据成员模板。与其他静态成员一样,静态数据成员模板的需要一个定义。这种定义可以在类定义外提供:

  • 静态数据,成员模板,静态成员没有加const需要在类外中初始化
struct Limits
{
	template<typename T>   // 静态数据的 成员变量模板
	static T max;
};
template<typename T>
const T Limits::max = {};  //注意 注意 注意,这时不需要加static
  • 类模板的非静态数据成员
template<typename T>
struct Foo
{
	static const T foo;
};
template<typename T>
const T Foo<T>::foo =   //是类模板,一定要与“静态成员,成员模板”做好区分,是在哪里进行显示转换

其实如果静态变量声明的是const,并且有初始值,那么可以不用在内外定义,会自动内联。(注意此时不能在外面声明);

class Test
{
public:
	static const int count = 0;          
};
cout << Test::count << endl;	//可以直接使用

在C++14之前没有变量模板之前

在 C++14 引入变量模板前,参数化变量通常实现为类模板的静态数据成员,或返回所需值的 constexpr 函数模板

#include <iostream>

using namespace std;

//1,使用变量模板
template<typename T>
constexpr T PI = T(3.14159265358);

//2,使用函数模板
template<typename T>
constexpr T getPI()
{
	return T(3.14159265358);
}

//3,使用类模板
template<typename T>
struct Math
{
	static constexpr T PI = T(3.14159265358);
};

int main()
{
	cout << PI<int> << " " << PI<float> << endl;
	cout << getPI<int>() << " " << getPI<float>() << endl;
	cout << Math<int>::PI << " " << Math<float>::PI << endl;

	/*
	3 3.14159
	3 3.14159
	3 3.14159
	*/
	return 0;
}

类型萃取(简单了解)

类型萃取,可简单理解为类型获取。对不同类型对象进行不同处理,可以提升程序效率,要注意的是:是在编译的时候确定类型,这也是元编程的核心思想之一,在编译的时候去区分、确定类型,然后针对不同类型做不同处理。

is_pod

萃取的典型应用是在模板函数中区分T的类型是原生类型POD还是自定义类型,POD全称plain old data,简单理解就是C++从C继承来的基本数据类型,如int、double等

之所以需要区分类型,主要是因为POD类型与自定义类型的很多处理方法不同,典型的就是copy,POD可以直接使用C库提供的memcpy,它主要是实现内存层面的拷贝,而非POD类型需要使用for循环挨个拷贝,因为涉及到深拷贝与浅拷贝的问题,所以在模板中需要识别数据类型,再做不同处理。

如以下代码,用来判断int类型是不是POD类型,有三种使用方式:

cout << std::boolalpha << is_pod<int>::value << endl;	//获取静态成员
cout << std::boolalpha << is_pod<int>() << endl;		//类型转换函数
cout << std::boolalpha << is_pod<int>()() << endl;		//operaotr()函数

输出结果

true
true
true

说明int确实是POD类型。

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

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

相关文章

leetcode155:最小栈

设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获取堆栈顶部的元素。int get…

探秘机器学习算法:智慧背后的代码逻辑

1、 线性回归 线性回归是预测连续变量的一种简单而有效的方法。其数学模型假设因变量 y 与自变量 x 之间存在线性关系&#xff0c;用公式表示为&#xff1a; ​ Python代码实现 import numpy as np from sklearn.linear_model import LinearRegression import matplotlib.…

Spring中@Import和@ComponentScan注解差异

首先我们定义两个类 进行Component扫描 返回结果 进行Import导入 返回 结果 可以看 我们在对该类的所有bean加载没有任何问题 结果一致 但神奇的地方在于此时 我们把Tiger类头的Component注解去掉 ComponentScan注解无法识别Tiger中的Lion Bean 删掉Component 再进行ComonentS…

Ceph 学习指南 集群部署【 cephadm 】

文章目录 引言初识 Server SANServer SAN 和传统存储对比 Ceph 概述Ceph 的架构设计Ceph 的特点Ceph 块存储Ceph 文件系统Ceph 对象存储Ceph 介绍 Ceph 集群部署配置 aliyun 源配置时间同步配置 hosts 文件安装 docker配置免密登录ceph 集群部署ceph1 配置安装 python3安装 cep…

(JVM)在JVM中,类是如何被加载的呢?本篇文章就带你认识类加载的一套流程!

在讲类加载前&#xff0c;需要先了解一下方法区、堆和直接内存三块内存区域的运行模式 1. 方法区 JVM中的方法去是所有线程中共享的一块区域 它存储了跟类相关的信息 方法区 会在虚拟机被启动时创建。它逻辑上是堆的组成部分 它在不同的jvm厂商中存在的位置可能会不同&…

【Arduino】一分钟快速在vs code 编译开发Arduino

下载Arduino 对于一些开发者来说&#xff0c;Arduino开发较为不方便&#xff0c;不管从代码的阅读性、开发效率等等方面&#xff0c;vs code都要优于Arduino IDE开发&#xff0c;而且vs code开发可以使用插件&#xff0c;比如一些AI代码插件&#xff0c;可以加快开发速率&#…

qt QDialog详解

1、概述 QDialog是Qt框架中用于创建对话框的类&#xff0c;它继承自QWidget。QDialog提供了一个模态或非模态的对话框&#xff0c;用于与用户进行交互。模态对话框会阻塞其他窗口的输入&#xff0c;直到用户关闭该对话框&#xff1b;而非模态对话框则允许用户同时与多个窗口进…

去除windows系统桌面字体的黑影

然后点开设置&#xff0c;关闭以下的2个选项

ssm034学生请假系统+jsp(论文+源码)_kaic

毕 业 设 计&#xff08;论 文&#xff09; 题目&#xff1a;学生请假系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本学生请假系统就是在这…

如何利用8款工具辅助建立需求管理体系

本文中&#xff0c;分享了8款辅助建立需求管理体系的工具&#xff1a;1.PingCode&#xff1b;2.Worktile&#xff1b;3.Jira&#xff1b;4.Trello&#xff1b;5.ClickUp&#xff1b;6.Notion&#xff1b;7.蓝鲸智云&#xff1b;8.红橘。 在如今快速发展的商业环境中&#xff0c…

使用Flask构建RESTful API

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Flask构建RESTful API Flask简介 环境搭建 安装Flask 项目结构 创建应用 路由定义 请求处理 获取查询参数 获取请求体 响应…

基于LLaMA Factory对LLama 3指令微调的操作学习笔记

一、环境 在vscode中用连接云服务器&#xff0c;打开文件目录。 df -h #查看盘容量 二、下载LLaMA Factory框架和数据 下载LLaMA Factory到云服务器 git clone https://github.com/hiyouga/LLaMA-Factory.git cd LLaMA-Factory pip install -e . pip install -e .命令的含…

解决VMwareWorkstation17中CentOS7不能上网的问题

在CentOS 7中查看网络状态&#xff0c;执行命令&#xff1a; systemctl status network.service 示例如下&#xff1a; [rootlinux ~]# systemctl status network.service 发现网络错误&#xff0c;解决办法&#xff1a; [rootlinux ~]# chkconfig NetworkManager off [rootli…

UI设计公司—兰亭妙微—提供轨道交通行业UI设计

蓝蓝设计工作室2008年开始&#xff0c;2011年正式成立北京兰亭妙微科技有限公司&#xff0c;主创清华团队&#xff0c;专注软件和互联网ui设计开发&#xff0c;擅长企业信息化管理、监控、大数据软件UIUE咨询和设计开发服务。立足UI&#xff0c;一直在学习进步。交通行业UE UI解…

Linux初阶——线程(Part3):POSIX 信号量 CP 模型变体

一、什么是 POSIX 信号量 信号量本质就是一个统计资源数量的计数器。​​​​​​​ 1、PV 操作 pv操作就是一种让信号量变化的操作。其中 P 操作可以让信号量减 1&#xff08;如果信号量大于 0&#xff09;&#xff0c;V 操作可以让信号量加 1. 2、信号量类型——sem_t 3…

【C语言】预处理(预编译)详解(下)(C语言最终篇)

文章目录 一、#和##1.#运算符2.##运算符 二、预处理指令#undef三、条件编译1.单分支条件编译2.多分支条件编译3.判断符号是否被定义4.判断符号是否没有被定义 四、头文件的包含1.库头文件的包含2.本地头文件的包含3.嵌套包含头文件的解决方法使用条件编译指令使用预处理指令#pr…

ComfyUI和Photoshop相结合,PS内实现:文生图,图生图,高清放大,局部重绘,面部修复,设计师福音

本文主要介绍&#xff1a;ComfyUI和Photoshop相结合&#xff0c;一个平台实现&#xff1a;图像生成&#xff0c;放大&#xff0c;局部重绘&#xff0c;面部修复&#xff0c;实时绘画 简直是设计师的福音。 主要包括&#xff1a; Photoshop 的安装以及插件的安装 Creative Cl…

音视频入门基础:AAC专题(11)——AudioSpecificConfig简介

音视频入门基础&#xff1a;AAC专题系列文章&#xff1a; 音视频入门基础&#xff1a;AAC专题&#xff08;1&#xff09;——AAC官方文档下载 音视频入门基础&#xff1a;AAC专题&#xff08;2&#xff09;——使用FFmpeg命令生成AAC裸流文件 音视频入门基础&#xff1a;AAC…

Git (推送到远端仓库)

目录 一、在 gitee 上创建一个仓库 二、将项目推送到远程仓库 三、解释推送命令 一、在 gitee 上创建一个仓库 操作如下&#xff1a; 二、将项目推送到远程仓库 这里例举新的项目推送到远程仓库的例子&#xff1a; 打开仓库查看&#xff1a; 三、解释推送命令 添加远程仓库…

qt QAction详解

1、概述 QAction是Qt框架中的一个抽象类&#xff0c;用于表示用户界面中的一个动作&#xff08;action&#xff09;。这些动作可以绑定到菜单项、工具栏按钮或快捷键上&#xff0c;提供了一种灵活的方式来处理用户交互。QAction不仅包含了动作的名称、图标、提示信息等属性&am…