当前位置: 首页 > article >正文

C++ 的 if-constexpr

1 if-constexpr 语法

1.1 基本语法

​ if-constexpr 语法是 C++ 17 引入的新语法特性,也被称为常量 if 表达式或静态 if(static if)。引入这个语言特性的目的是将 C++ 在编译期计算和求值的能力进一步扩展,更方便地实现编译期的分支选择动作。早期的 C++ 缺少类似的语言特性,C++ 开发者不得不使用 tag dispatching 这样的惯用手法或者借助于模板特化机制让编译器在模板参数推导时“曲折地”实现一些静态选择。C++ 11 标准明确了 SFINAE 机制的应用,并提供了 enable_if ,二者配合可以更方便地实现编译期的分支选择,但是与 if-constexpr 相比,可读性和易用性都差了几条街。

​ 在介绍 if-constexpr 的“神通”之前,前来看一下 if-constexpr 的语法形式。其实我们在介绍如何让自己的设计的类型支持结构化绑定的时候(《让自定义类型支持结构化绑定》),为实现 get<N>() 成员方法,就用到了这种语法,这里再回顾一下 if-constexpr 语法的表达形式:

struct FooTest
{template<std::size_t N>const auto& get() const{if constexpr (N == 0) return name;else if constexpr (N == 1) return age;  //else if 分支中的 constexpr 说明符可以省略else return weight;}
};

这个函数中对模板参数 N 的判断在编译期间进行,由哪个分支返回数据也是在编译期间就决定了,false 分支中的代码甚至不会出现在实例化后的函数代码中。以get<N>()成员函数为例,如果使用 FooTest 的代码用到 get<0>() 取 name 这个成员,则最终实例化后会得get<0>()的特化实现:

struct FooTest
{template<>const std::string& get<0>() const{return name;}
};

如果代码中还用了get<1>()取 age 的值,则编译器还会实例化出 get<1>()这个特化版本:

template<>
const int& get<1>() const
{return age;
}

1.2 扩展说明

1.2.1 条件表达式

​ 常量 if 表达式中的条件表达式必须是 bool 类型的常量表达式,或者是可转换成 bool 类型的常量表达式。因为这个条件表达式是在编译期进行评估的,所以 constexpr 修饰的常量或函数都可以出现在这个表达式中,但是运行期间的变量或非 constexpr 的函数不可以出现在条件表达式中。

​ C++ 17 中,lambda 表达式(lambda 函数)可以被显式声明为 constexpr,但是当一个 lambda 表达式和相关的上下文一起达成一个闭包时,如果这个闭包在一个 constexpr 上下文中使用,即使没有将其显式声明为 constexpr,它也会被当作 constexpr 使用,比如:

auto DoubleIt = [](int n)  {  return n + n;  };
template<std::size_t N>
bool Func2()
{if constexpr (DoubleIt(N) < 100)return true;elsereturn false;
}std::cout << Func2<10>() << ", " << Func2<50>() << std::endl;  //1, 0

1.2.2 false 分支处理

​ 函数模板实例化时,评估为 false 分支的语句不会出现在最终实例化后的函数代码中,但是编译器会对其进行语法检查,当出现语法错误时也会报错(有些编译器报错)。虽然会进行语法检查,但是 false 分支中的 return 语句不会参与函数的返回值类型推定,比如这个例子:

template<typename T>
auto get_value(T t)
{if constexpr (std::is_pointer_v<T>)return *t; elsereturn t; 
}

std::is_pointer_v<T>为 true 时,else 分支中的 return t 语句不参与返回值类型推定,函数返回值类型推定为 *t 的类型。当std::is_pointer_v<T>为 false 时正好相反,else 分支中的 return t 语句将决定返回值类型,函数返回值类型推定为 t 的类型。这里需要注意,如果取消配合 if-constexpr 的 else 分支,改用函数设计常用的默认返回方式,编译器就会报错,比如:

template<typename T>
auto get_value(T t)
{if constexpr (std::is_pointer_v<T>)return *t; return t; 
}

这个 get_value2() 函数的代码语义与前面的 get_value() 函数一样,但是编译器会报错,因为最后的 return 语句也参与返回值类型的推定,并且当 T 是指针类型的时候,两个 return 语句的返回值类型推定会互相矛盾。

​ 尽管编译器会对 false 分支的代码进行语法检查,但是 false 分支的代码会被丢弃,所以不参与代码链接。比如这个例子:

extern int x; //int f()
{if constexpr (true)return 0;else if (x)return x;elsereturn -x;
}f(); //调用 f

尽管全局变量 x 只有一个 extern 声明,并没有定义,但是这段代码编译正常,没有错误。因为 else if 和 else 分支的代码都被丢弃,编译器没有为链接代码而定位 x 的需要。

​ 编译器之所以要对准备丢弃的 false 分支代码进行语法检查,可能原因是它要对整个 if 语句进行分析,了解每个分支逻辑的起始位置和结束位置,以便能够正确地保留 true 分支的代码,丢弃 false 分支的代码。

1.2.3 初始化语句

​ C++ 17 开始支持在 if 语句中使用初始化语句,当然,if-constexpr 语法上也支持初始化语句,只是要求初始化语句也只能使用常量和常量表达式。比如这个例子中的 k 的初始化:

template<std::size_t N>
bool Func()
{if constexpr (constexpr std::size_t k = 3; (N % k) == 0)return true;elsereturn false;
}

k 必须是个常量,初始化 k 的表达式必须是常量表达式。

2 if-constexpr 的作用

​ 可在编译期执行的 if-constexpr,用于模板元编程中的条件判断,不仅扩展了模板元编程的分支处理能力,也简化了很多以前用非常复杂方式实现的分支判断代码,使得模板元编程对条件分支的处理代码更直观,更容易理解。这一部分我们用三个例子,分别介绍一下 if-constexpr 的作用,包括对传统的 tag dispatching 和模板特化习惯用法的比较。

2.1 简化可变参数的处理方式

​ 使用 if-constexpr 可以提高泛型代码的可读性,上一节介绍的 get<N>() 的函数,如果不用 if-constexpr,就需要借助于模板的递归推导来解决 N 的个数不确定问题。具体做法就是定义一个泛化版本加上一个特化版本,这样实现起来不仅麻烦,代码可读性也大打折扣。这一节,我们用一个之前介绍过的用于求和的函数模板为例,介绍一下 if-constexpr 对可变参数的处理以及提高代码可读性能带来的好处。

​ 在 C++ 17 之前,没有折叠表达式和 if-constexpr,对参数包的处理需要用到模板的递归推导,需要定义一个结束递归推导的特化实例,非常不直观:

template<typename T>
auto Sum(T arg)
{return arg;
}template<typename T, typename... Args>
auto Sum(T arg, Args... args_left)
{return arg + Sum(args_left...);
}std::cout << Sum(3, 5, 8) << std::endl;  //输出16
std::cout << Sum(std::string("Emma "), std::string(" love cats!")) << std::endl; //输出 Emma love cats!

C++ 17 引入了折叠表达式,用了折叠表达式就简单多了,看看折叠表达式的版本:

template<typename... Args>
auto Sum(Args&&... args)
{return (0 + ... + args);
}

但是折叠表达式的语法让很多初学者“毛骨悚然”,如果改成用 if-constexpr 实现,则代码更符合直觉,可读性也上了一个台阶:

template <typename T, typename... Args>
auto Sum(T arg, Args... args)
{if constexpr (0 == sizeof...(Args))return arg;elsereturn arg + Sum(args...);
}

2.2 比std::enable_if 更灵活

​ SFINAE (Substitution Failure Is Not An Error)的意思就是模板推导的过程中,如果模板参数替换后得到一个无意义的错误结果,编译器并不立即报错,而是暂时忽略这个模板函数声明,继续参数推导和替换。C++ 11 引入的 std::enable_if 就是实现 SFINAE 的最直接方式,下面用 ToString() 函数为例(注意,这不是一个严谨的实现,只是作为一个例子),看看如何用 std::enable_if 实现编译期的条件分支。

//也可以用 enable_if_t
template<typename T>
std::enable_if<std::is_arithmetic<T>::value, std::string>::type
ToString(T t)
{return std::to_string(t);
}template<typename T>
std::enable_if<!std::is_arithmetic<T>::value, std::string>::type
ToString(T t)
{return t;
}

std::to_string() 支持将一个整数型数据或浮点数型数据转成字符串,如果 T 本身就是 std::string 类型,则不需要转换。std::enable_if 的作用就是通过对返回值类型的控制,使得当类型 T 与函数代码不匹配(比如 to_string() 函数不支持 std::string 类型)的时候产生一个错误的函数声明。举个例子,当 T 是 std::string 类型时(不是数字类型),编译器对两个模板函数进行参数替换后得到两个函数声明:

template<>
ToString<std::string>(std::string t);template<>
std::string ToString<std::string>(std::string t);

第一个替换结果没有函数返回值,是个语法上错误的函数声明,编译器会丢弃这个替换结果,选择第二个语法上正确的作为最终的 ToString() 函数重载裁决结果。如此一来,就通过 std::enable_if 与 SFINAE 机制配合,实现了编译期分支选择的目的。

​ 但是使用 std::enable_if 控制需要注意一点,std::enable_if 只能将条件分割成两种情况,就是两个条件必须互斥,即一个是 true 条件,另一个必须是 false 条件,否则一旦出现两个判断条件都是 true 的情况,就会出现两个正确的结果,导致编译器报告“模棱两可的函数调用” 的编译错误。通过上面的例子可以看出,尽管 std::enable_if 也能实现编译期的条件分支选择,但是代码并不直观,约束条件比较多,且只能实现两个分支的选择。现在看看使用 if-constexpr 的实现方案:

template<typename T>
auto ToString(T t)
{if constexpr (std::is_arithmetic<T>::value)return std::to_string(t);elsereturn t;
}

这样的代码要比写两个重载函数让编译器按照 SFINAE 原则匹配调用的方式更直观,也更容易理解和维护。

2.3 比 tag dispatching 更直观

​ tag 就是一些没有数据,没有操作的空类型,它们可以作为函数参数来影响编译器对重载函数的选择。用 tag dispatching 技术首先要定义 tag,根据本文的例子,我们定义两个 tag:

struct NumTag {};
struct StrTag {};

接着要定义重载函数,唯一不同的参数就是 tag 类型,tag 类型作为函数的哑形参只影响编译器对重载函数的选择,最终这个没有的参数都会被编译器优化掉:

template <typename T>
auto ToString_impl(T t, NumTag)
{return std::to_string(t);
}template <typename T>
auto ToString_impl(T t, StrTag)
{return t;
}

最后就是实现 ToString(),根据 T 的类型确定是调用 ToString_impl(t, NumTag()); 还是调用 ToString_impl(t, StrTag());,具体做起来就八仙过海,各显神通,比如这个使用自定义 type_traits 的方式:

template <typename T> //一个并不严谨的泛化版本
struct traits
{typedef NumTag tag;
};template <>   //针对 std::string 的特化版本
struct traits<std::string>
{typedef StrTag tag;
};template <typename T>
auto ToString(T t)
{return ToString_impl(t, typename traits<T>::tag());  //根据 traits<T>::tag 选择 ToString_impl()
}

​ 对比上一节用 if constexpr 实现的版本,可以看出来使用 tag dispatching 的代码比较晦涩,需要研究一下 tag 的定义才能了解分支选择的具体条件,代码实现不如 if constexpr 直观。

3 if-constexpr 与 if 的区别

3.1 if 为什么不行

​ 上一节的 ToString() 函数的例子如果不用 if-constexpr,像这样直接用 if 实现:

template<typename T>
auto ToString(T t)
{if (std::is_arithmetic<T>::value)return std::to_string(t);elsereturn t;
}

是否也可以呢?答案是不可以,因为 std::is_arithmetic<T> 是在编译期求值的,当代码中需要将整数 42 转成字符串,调用 ToString(42) 的时候,传入参数是 int 或 double,评估结果是 true,此时函数模板被实例化成:

auto ToString(int t)
{if (true)return std::to_string(t);elsereturn t;
}

这个实例化结果是无法编译的,因为返回值到底是整数还是 std::string 呢?两个 return 语句的返回值类型不一致。再看看到传入参数是 std::string 的情况,此时 if 的评估结果是 false,函数模板被实例化成:

auto ToString(std::string t)
{if (false)return std::to_string(t);elsereturn t;
}

尽管走 else 分支,直接返回 t 没有问题,但是 if 分支的编译会有问题,因为 std::to_string() 不支持 std::string 类型。所以,直接使用 if 语句是不可以的。

3.2 if-constexpr 为什么可以

​ 现在对比使用 if-constexpr 的情况。前面提到过,对于 false 分支编译只进行语法分析,不生成代码。所以当代码中出现 ToString(42) 的调用的时候,传入参数是 int,评估结果是 true,此时函数模板被实例化成:

std::string ToString(int t)
{return std::to_string(t);
}

当传入参数是字符串类型的时候,else 分支就成为 true 分支被保留,函数模板实例化的结果就是:

std::string ToString(std::string t)
{return t;
}

最终实例化的结果和使用 std::enable_if 的结果是一样的,但是语法比 std::enable_if 简单,直观。

4 if-constexpr 与 #if 的区别

​ 编译期 if 表达式很容易让人想到 C++ 的条件编译指令 #if,但是它们的区别还是很明显的,主要有三点:

  • 处理阶段不同:#if 条件编译指令是在代码预处理阶段解析的,预处理器处理完成后提交给编译器时,编译器只能看到 true 分支的内容,而 if-constexpr 的代码都是在编译阶段进行处理的;
  • 条件表达式要求不同:首先是代码处理的阶段不一样,#if 只能使用用于定义的宏、编译器预定义的宏和环境变量,不能使用代码中的函数或变量,而 if-constexpr 的条件表达式可以是代码中的常量,或者常量函数;
  • false 分支的处理方式不同:条件编译中的 false 分支编译器不进行语法检查,实际上它们在预编译阶段就被过滤掉了,而 if-constexpr 中的 false 分支也进行语法检查。

关注作者的算法专栏
https://blog.csdn.net/orbit/category_10400723.html

关注作者的出版物《算法的乐趣(第二版)》
https://www.ituring.com.cn/book/3180

相关文章:

C++ 的 if-constexpr

1 if-constexpr 语法 1.1 基本语法 ​ if-constexpr 语法是 C 17 引入的新语法特性&#xff0c;也被称为常量 if 表达式或静态 if&#xff08;static if&#xff09;。引入这个语言特性的目的是将 C 在编译期计算和求值的能力进一步扩展&#xff0c;更方便地实现编译期的分支…...

涨薪技术|k8s设计原理

01k8s介绍 Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化 工作负载和服务&#xff0c;有助于实现声明性配置和自动化。它有一个庞大、快速增长的生态系统。Kubernetes 服务、支持和工具广泛可用。Kubernetes 这个名字起源于希腊语&#xff0c;意思是舵…...

基于FPGA的16QAM+帧同步系统verilog开发,包含testbench,高斯信道,误码统计,可设置SNR

目录 1.算法仿真效果 2.算法涉及理论知识概要 2.1 16QAM调制解调原理 2.2 帧同步 3.Verilog核心程序 4.完整算法代码文件获得 1.算法仿真效果 vivado2019.2仿真结果如下&#xff08;完整代码运行后无水印&#xff09;&#xff1a; 设置SNR12db 将FPGA数据导入到MATLAB显…...

QuecPython 外设接口之GPIO应用指南

基础知识 了解GPIO基础知识更有益于我们使用它。 框图 GPIO&#xff08;通用输入输出&#xff09;是指一种通用的数字输入/输出接口&#xff0c;用于与外部电子元件或设备进行通信。它通常存在于微处理器、微控制器和其他嵌入式系统中。 物理电路结构如下图所示&#xff1a…...

Spring Boot 整合 Nacos 注册中心终极指南

在微服务架构中&#xff0c;配置管理和动态路由是核心需求。Nacos 作为阿里巴巴开源的动态服务发现、配置管理和服务管理平台&#xff0c;能够帮助开发者实现配置热更新、多环境共享配置以及动态路由管理。本文将结合 Spring Boot 和 Spring Cloud Gateway&#xff0c;手把手教…...

清晰易懂的 Maven 彻底卸载与清理教程

一、Windows 系统卸载 Maven 步骤 1&#xff1a;删除 Maven 安装目录 找到 Maven 的安装路径&#xff08;默认可能为 C:\Program Files\apache-maven-3.x.x 或自定义路径&#xff09;。直接删除整个 Maven 文件夹&#xff08;如 apache-maven-3.x.x&#xff09;。 步骤 2&am…...

光流 | 基于KLT算法的人脸检测与跟踪原理及公式,算法改进,matlab代码

===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 人脸检测与跟踪 一、KLT算法原理与分析1. 核心思想2. 数学模型二、人脸…...

Spring MVC请求与响应全解析:从参数绑定到异常处理

文章目录 一、请求映射的艺术&#xff1a;RequestMapping深度解析1. 多级路径配置2. 六大核心属性3. RESTful风格实践 二、参数绑定黑科技1. 智能绑定机制基础类型绑定对象嵌套绑定集合类型绑定 2. 参数处理三剑客 三、响应处理全攻略1. 视图跳转三种模式2. JSON交互实践 四、文…...

用免费的github的key调用gpt实现一个简单的rag自动打分评测系统,不用任何框架

1.环境准备 !pip install pymupdf numpy openai 2.导入依赖 import fitz import os import numpy as np import json from openai import OpenAI 3.pdf提取文本 def extract_text_from_pdf(pdf_path):"""从 PDF 文件中提取文本内容。参数:pdf_path (str): …...

SQLServer列转行操作及union all用法

1.创建测试表及数据sql如下 create table ScoresTable( Name varchar(50), ChineseScore int, MathScore int ) insert into ScoresTable values(小张,90,95) insert into ScoresTable values(小王,98,99) 2.表中查询结果如下 3.现需列转行显示&#xff0c;每行显示 姓名…...

深度学习框架PyTorch——从入门到精通(6.2)自动微分机制

本节自动微分机制是上一节自动微分的扩展内容 自动微分是如何记录运算历史的保存张量 非可微函数的梯度在本地设置禁用梯度计算设置requires_grad梯度模式&#xff08;Grad Modes&#xff09;默认模式&#xff08;梯度模式&#xff09;无梯度模式推理模式评估模式&#xff08;n…...

Java面试10个“隐藏考点”

1. Java模块化系统&#xff08;JPMS&#xff09;的requires transitive作用 问题&#xff1a;如何在模块化项目中传递依赖&#xff1f; 解析&#xff1a; ​**requires transitive**&#xff1a;声明模块的依赖可被下游模块隐式继承。​示例&#xff1a;模块A依赖模块B并添加…...

【GL010】C++

1.C中的const关键字有哪些用法&#xff1f; 1.修饰变量&#xff1a;表示变量的值不可修改。 const int a 10; 2.修饰指针&#xff1a; const int* p&#xff1a; // 指针指向的内容不可修改。 int* const p&#xff1a; // 指针本身不可修改。 const int* const…...

(Arxiv-2025)MagicDistillation:用于大规模人像少步合成的弱到强视频蒸馏

MagicDistillation&#xff1a;用于大规模人像少步合成的弱到强视频蒸馏 paper是HKUST发布在Arxiv 2025的工作 paper title&#xff1a;MagicDistillation: Weak-to-Strong Video Distillation for Large-Scale Portrait Few-Step Synthesis Project page&#xff1a;地址 Abst…...

Linux paste命令

目录 一. 简介二. 基本语法三. 小案例 一. 简介 paste 命令用于合并多个文件的行&#xff0c;按列方式输出&#xff0c;默认以制表符&#xff08;Tab&#xff09;分隔。 ⏹基本语法 paste [选项] 文件1 文件2 ...二. 基本语法 <()的方式模拟文件流paste命令将2个文件流粘…...

大模型金融企业场景落地应用

一、商业银行体系 1. 江苏银行 企业背景&#xff1a;江苏银行是总部位于江苏南京的全国性股份制商业银行&#xff0c;在城商行中资产规模位居前列&#xff0c;积极拥抱金融科技&#xff0c;将数字化转型作为核心战略之一。近年来&#xff0c;江苏银行持续加大在人工智能、大数…...

Excel(进阶篇):powerquery详解、PowerQuery的各种用法,逆透视表格、双行表头如何制作透视表、不规则数据如何制作数据透视表

目录 PowerQuery工具基础修改现有数据理规则PowerQuery抓取数据的两种方式多文件合并透视不同表结构多表追加数据透视追加与合并整理横向表格:逆透视 数据用拆分工具整理数据算账龄 不等步长值组合合并文件夹中所有文件PowerQuery处理CSV文件双行表头、带合并单元格如何做数据…...

神经网络解决非线性二分类

这份 Python 代码实现了一个简单的神经网络&#xff0c;用于解决复杂的非线性二分类问题。具体步骤包含生成数据集、定义神经网络模型、训练模型、测试模型以及可视化决策边界。 依赖库说明 python import numpy as np import matplotlib.pyplot as plt from sklearn.datase…...

CentOS 8.2 上安装 JDK 17 和 Nginx

AI越来越火了&#xff0c;我们想要不被淘汰就得主动拥抱。推荐一个人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;最重要的屌图甚多&#xff0c;忍不住分享一下给大家。点击跳转到网站 一、安装 JDK 17 1. 使用 dnf 安装&#xff08;推荐&#xff09…...

Python 爬虫(4)HTTP协议

文章目录 一、HTTP协议1、HTTP特点2、HTTP工作原理3、HTTP与HTTPS的区别 前言&#xff1a; HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是互联网上应用最为广泛的一种网络协议&#xff0c;用于在客户端和服务器之间传输超文本&#xf…...

Simple-BEV的bilinear_sample 作为view_transformer的解析,核心是3D-2D关联点生成

文件路径models/view_transformers 父类 是class BiLinearSample(nn.Module)基于https://github.com/aharley/simple_bev。 函数解析 函数bev_coord_to_feature_coord的功能 将鸟瞰图3D坐标通过多相机&#xff08;针孔/鱼眼&#xff09;内外参投影到图像特征平面&#xff0…...

Midscene.js自然语言驱动的网页自动化全指南

一、概述 网页自动化在数据抓取、UI 测试和业务流程优化中发挥着重要作用。然而&#xff0c;传统工具如 Selenium 和 Puppeteer 要求用户具备编程技能&#xff0c;编写复杂的选择器和脚本维护成本高昂。Midscene.js 通过自然语言接口革新了这一领域&#xff0c;用户只需描述任…...

同一个局域网的话 如何访问另一台电脑的ip

在局域网内访问另一台电脑&#xff0c;可以通过以下几种常见的方法来实现&#xff1a; ‌直接通过IP地址访问‌&#xff1a; 首先&#xff0c;确保两台电脑都连接在同一个局域网内。获取目标电脑的IP地址&#xff0c;这可以通过在目标电脑上打开命令提示符&#xff08;Windows系…...

基于SpringBoot的名著阅读网站

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…...

Excel(实战):INDEX函数和MATCH函数、INDEX函数实战题

目录 经典用法两者嵌套查值题目解题分析 INDEX巧妙用法让数组公式&#xff0c;自动填充所有、有数据的行/列INDEX函数和SEQUENCE函数 经典用法两者嵌套查值 题目 根据左表查询这三个人的所有数据 解题分析 INDEX函数的参数&#xff1a;第1个参数是选定查找范围&#xff0c…...

希尔排序中的Hibbard序列

一 定义 Hibbard序列的每个元素由以下公式生成: h_k = 2^k - 1 其中k从1开始递增,序列为:1, 3, 7, 15, 31, 63, … 二 生成方式 起始条件:k=1,对应h_1=2^1-1=1 递推公式:每次k增加1,计算 h_{k+1}=2^{k+1}-1 示例:前5项…...

uniapp超简单ios截屏和上传app store构建版本方法

​ 假如使用windows开发ios的应用&#xff0c;上架的时候&#xff0c;你会发现&#xff0c;上架需要ios应用多种尺寸的ios设备的截图&#xff0c;和需要xcode等工具将打包好的ipa文件上传到app store的构建版本。 大部分情况下&#xff0c;我们的公司都没有这么多款ios设备来…...

Netty源码—5.Pipeline和Handler一

大纲 1.Pipeline和Handler的作用和构成 2.ChannelHandler的分类 3.几个特殊的ChannelHandler 4.ChannelHandler的生命周期 5.ChannelPipeline的事件处理 6.关于ChannelPipeline的问题整理 7.ChannelPipeline主要包括三部分内容 8.ChannelPipeline的初始化 9.ChannelPi…...

Netlify 的深度解析及使用指南

以下是关于 Netlify 的深度解析及使用指南&#xff0c;结合其核心功能与用户需求&#xff0c;提供一站式解决方案&#xff1a; 一、Netlify 核心优势 全托管静态网站服务Netlify 提供从代码托管、自动化构建到全球 CDN 加速的全流程服务&#xff0c;支持 HTML/CSS/JS 静态资源及…...

MySQL小练习

目录 一、单表查询 二、多表查询 一、单表查询 素材&#xff1a; 表名&#xff1a;worker-- 表中字段均为中文&#xff0c;比如 部门号 工资 职工号 参加工作 等 CREATE TABLE worker ( 部门号 int(11) NOT NULL, 职工号 int(11) NOT NULL, 工作时间 date NOT NULL, 工资 float…...