现代C++讲解之enum class,static_assert,tuple的使用

本篇目标:

认识enum class,static_assert,tuple及其使用

一.强类型枚举(enum class)

1.传统枚举

C++枚举存在以下主要问题:

<1>. 隐式转换为整型:枚举值会自动转换为整数,可能导致意外行为

<2>. 污染外围作用域:枚举值会泄漏到包含它的作用域中

<3>. 无法指定底层类型:不能明确控制枚举使用的存储大小

例如:

cpp 复制代码
enum Color { Red, Green, Blue };
enum TrafficLight { Red, Yellow, Green };

int main()
{

	//使⽤enum是不可以这样定义的,enum值是暴露到全局的,Red存在冲突
    Color c1=Red;
	int a=Color::Red;		//C语言中的枚举即使加了::也会发生重定义
	//隐式转换为整型:枚举值会⾃动转换为整数,可能导致意外⾏为
	std::cout << a << std::endl;
	return 0;
}

Color与TrafficLight的部分内容冲突了,所以在使用时,就会出错

2.强类型枚举

规则:

<1>.拥有**强作用域,**枚举值被限制在枚举类型的内部,访问时必须加上枚举类型名作为前缀和域操作符,例如:

cpp 复制代码
enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green }; 

int main()
{
    Color c = Color::Red;           // 必须带上 Color:: 作用域
    TrafficLight t = TrafficLight::Red;
    return 0;
}

<2>.强类型, 不会隐式转换为 int 或其他枚举类型,如果要转换,必须明确告诉编译器你要这么做,例如:

cpp 复制代码
#include<iostream>
using namespace std;
enum class Color { Red, Green, Blue };
enum class TrafficLight { Red, Yellow, Green }; 

int main()
{
    Color c = Color::Red;           // 必须带上 Color:: 作用域
    TrafficLight t = TrafficLight::Red;
    //int a=Color::Red;        //报错
    int a=static_cast<int>(Color::Red);
    cout<<a<<endl;
    return 0;
}

<3>.可以指定底层类型,默认情况下,enum class 的底层类型是 int。但你可以显式指定它的底层类型,例如:

cpp 复制代码
enum class SmallEnum : uint8_t { Value1, Value2 };  // 8位存储
enum class BigEnum : uint32_t { Value1, Value2 };   // 32位存储

注意:enum class(强类型枚举)的内存对齐数取决于它的底层类型。

<4>.C++20后,可以引入强枚举到当前作域,例如:

cpp 复制代码
using enum Color;  
Color c = Red;

二.static_assert

概念: static_assert 是C++11引入的编译时断言机制 ,它允许开发者在编译期间检查条件是否满足,如 果条件不满足,则会导致编译错误。

使用规则:

cpp 复制代码
static_assert(常量表达式, 错误消息);

注意:C++17后,错误信息可以不写

1.使用样例:

1.如果你的函数或变量是用 constexpr 修饰的(即在编译期就能算出结果),你可以用 static_assert 对其结果进行验证,例如:

cpp 复制代码
// 在编译期就可以计算阶乘的函数
constexpr int factorial(int n) 
{
    return n <= 1 ? 1 : (n * factorial(n - 1));
}

int main() 
{
    // 可以在在编译期验证逻辑是否正确
    static_assert(factorial(4) == 24, "计算错误!");
    static_assert(factorial(5) == 120, "计算错误!");
    return 0;
}

2.编写跨平台代码,或者你的底层逻辑对数据类型的大小有严格要求时,可以使用 static_assert保证编译环境符合预期,例如:

cpp 复制代码
// 确保指针大小为 8 字节(限制代码只能在 64 位系统上编译)
static_assert(sizeof(void*) == 8, "This code requires 64-bit platform");

3.限制模板参数的类型,防止别人传入错误的类型导致报错,例如:

cpp 复制代码
#include <iostream>
#include <type_traits> 

template<typename T>
void process(T value) 
{
    static_assert(std::is_integral<T>::value, "T must be an integral type");
    // 函数实现... 
}

int main() {
    std::cout << process(10) << std::endl;    
    
    // std::cout << process(3.14) << std::endl;     //报错
    // std::cout << process("C++") << std::endl;     //报错 
    
    return 0;
}

2.static_assert与assert的区别

|------|------------------------------|-------------------------------------|
| 对比 | static_assert | assert |
| 生效时间 | 编译期 | 运行期 |
| 条件要求 | 必须是编译期常量表达式 | 可以是任意的变量表达式 |
| 影响 | 编译报错 | 运行终止 |
| 主要用途 | 类型检查、模板参数约束、内存对齐验证、跨平台环境假设验证 | 验证函数的传入参数、验证指针是否为空、验证业务逻辑的中间状态 |
| 报错表现 | 编译器报错,生成带有我们自定义信息的报错日志 | 程序运行时突然崩溃 (Abort),并打印出导致崩溃的代码行号和表达式 |

注意:is_integral是类型萃取的内容,可以用来检查T的类型是否为整形,后续会讲的

三.tuple的使用

概念:在 C++ 中,std::tuple(元组)是 C++11 引入的一个非常实用的标准库组件,定义在 <tuple> 头文件中,你可以把它看作是 std::pair 的泛型升级版 ,或者是**一个没有名字的结构体 (Struct),**允许你将任意数量、任意类型的数据打包成一个单一的对象。

1.创建tuple

例如:

cpp 复制代码
// 创建⼀个包含3个元素的tuple : int, double, string
std::tuple<int, double, std::string> t1(1, 2.00003, "2134");

// 使⽤make_tuple⾃动推导类型
auto t2 = std::make_tuple(1, 2026, "123456");

// C++17起可以使⽤类模板参数推导
std::tuple t3("123456", 123, 12.0);		//自动推导为string,int,double
std::tuple t4("123456", 123, std::vector<int>(1));	

2.访问与修改元素

例如:

cpp 复制代码
int a = std::get<0>(t1);
std::string str = std::get<2>(t2);
std::cout << a << std::endl << str << std::endl;

//修改元素 (std::get 返回的是引用)
std::get<2>(t3) = 15.0;

// C++14起可以通过类型访问(类型必须唯⼀)
std::cout << std::get<int>(t1) << std::endl;// 输出100

std::cout << std::get<double>(t1) << std::endl;   // 输出3.14

3.解包tuple

例如:

cpp 复制代码
int x; double y;
std::string z;
std::tie(x, y, z) = t1;			//使用std::tie即可
std::cout << x << std::endl << y << std::endl << z << std::endl;

// C++17支持使用结构化绑定
auto [a1, b1, c1] = t2;
std::cout << a1 << std::endl << b1 << std::endl << c1 << std::endl;
相关推荐
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 142. 环形链表 II | C++ 哈希表直觉解法
c++·leetcode·链表
周杰伦fans2 小时前
深入 C# 匿名类型:从 `new { Ask = ask }` 说起
开发语言·c#
fish_xk2 小时前
c++中的继承
开发语言·c++
froginwe112 小时前
CSS 图像透明/不透明
开发语言
初心未改HD2 小时前
Go语言Map底层原理与并发安全深度解析
开发语言·golang
Brilliantwxx2 小时前
【算法题】日期类算法题
开发语言·c++·笔记·程序人生·算法
不会编程的懒洋洋2 小时前
C# IDisposable 和 using
开发语言·笔记·机器学习·c#·.net·visual studio·c#基础
Fighting_p2 小时前
【FileShowCom 组件】文件预览:图片预览 el-image,其余文件预览打开新窗口或者下载
开发语言·前端·javascript
XiYang-DING2 小时前
【Java EE】线程池
java·开发语言·java-ee