10.SFINAE
SFINAE一、SFINAE 核心定义SFINAE 直译是「替换失败不是错误」,是 C++ 模板重载解析阶段的关键规则: 当编译器尝试将模板参数替换为具体类型时,如果出现 “语法合法但替换失败” 的情况,不会直接报错,而是跳过这个模板重载版本,继续尝试其他可行的版本; 只有当所有模板重载版本都替换失败,且无其他非模板重载可用时,编译器才会抛出真正的编译错误。 通俗理解(无比喻版)模板重载就像 “多套备用方案”,编译器会逐个检查方案是否适配当前类型: 某套方案因类型不匹配导致替换失败 → 放弃这套方案,看下一套; 所有方案都适配失败 → 才报错。 二、核心原理:模板替换阶段 vs 编译阶段SFINAE 只作用于模板参数替换阶段,而非后续的语义分析 / 编译阶段,这是关键: 阶段 行为 SFINAE 是否生效 模板参数替换阶段 编译器将实参类型代入模板形参,检查语法是否合法(如成员是否存在、类型是否匹配) 生效(失败则跳过) 语义分析 / 编译阶段 检查代码逻辑(如变量未定义、函数调用错误) 不生效(直接报错) 三、SFINAE 的典型...
9.模板折叠
模板折叠(Fold Expressions)1. 折叠表达式的概念与背景在C++中,可变参数模板允许函数或类模板接受任意数量的模板参数。这在编写灵活且通用的代码时非常有用。然而,处理参数包中的每个参数往往需要递归模板技巧,这样的代码通常复杂且难以维护。 折叠表达式的引入显著简化了这一过程。它们允许开发者直接对参数包应用操作符,而无需手动展开或递归处理参数。这不仅使代码更加简洁,还提高了可读性和可维护性。 C++17 折叠表达式的分类依据是参与折叠的参数包数量,而非操作符的元数: 折叠表达式可分为: 一元折叠(Unary Fold):仅针对单个参数包,通过一个二元操作符将参数包中的所有元素按指定结合方向 “串联” 成连续表达式,无需额外固定值参与运算。 二元折叠(Binary Fold):针对 “一个参数包 + 一个额外固定值”,通过同一个二元操作符将固定值与参数包元素按指定结合方向串联成连续表达式,是一元折叠的扩展(解决带初始值的运算场景)。 一元操作符(!/~/++ 等)无法直接作为折叠表达式的 op,因为折叠的本质是 “将参数包的元素用操作符串联起来”...
8.模板特例化
模板特化(Template Specialization)模板特化允许开发者为特定类型或类型组合提供专门的实现。当通用模板无法满足特定需求时,特化模板可以调整行为以处理特定的情况。C++ 支持全特化(Full Specialization)和__偏特化(Partial Specialization),但需要注意的是,函数模板不支持偏特化,只能进行全特化 类模板的特例化类模板全特化123456789101112131415template<typename T> class Printer { public: void print(const T& obj) { std::cout<<"General Printer "<<obj<<std::endl; } }; template<> class Printer<std::string> { public: void p...
7.奇异递归模板(CRTP)
奇异递归模板(CRTP)一、什么是 CRTP?奇异递归模板模式(Curiously Recurring Template Pattern)是 C++ 中的一种高级模板技巧,核心特征是:一个类派生自以自身作为模板参数的基类。 代码实例 12345678910111213141516171819// 基类:模板类,接收派生类作为模板参数template <typename Derived>class Base {public: // 基类中可调用派生类的成员 void interface() { // 向下转型为派生类(安全,因为Derived必然是子类) static_cast<Derived*>(this)->implementation(); }};// 派生类:继承基类,且将自身作为基类的模板参数class Derived : public Base<Derived> {public: // 派生类实现具体逻辑 void imp...
6.stl常用函数
一、通用基础函数(几乎所有容器都支持)这些函数是所有容器的共性,是使用 STL 容器的基础: 函数 功能说明 empty() 判断容器是否为空,返回bool值(空返回true),时间复杂度 O (1) size() 返回容器中元素的个数,时间复杂度 O (1)(list/forward_list 除外,O (n)) clear() 清空容器中所有元素,释放内存(不同容器释放策略略有差异) begin() 返回指向容器第一个元素的迭代器(可读可写,非 const 容器) end() 返回指向容器末尾之后的迭代器(尾后迭代器,不可解引用) cbegin()/cend() 返回只读的 const 迭代器(不能通过迭代器修改元素) swap() 交换两个同类型容器的内容,时间复杂度 O (1)(底层交换内部指针,效率极高) 示例(通用函数): 1234567891011121314151617181920#include <iostream>#include <vector>using namespace...
5.C++字符串拼接和分割
C++字符串拼接和分割一、字符串拼接字符串拼接的核心需求是把多个不同类型(字符串、数字、字符等)的内容合并成一个字符串,C++ 有多种实现方式。 1. 推荐方式:ostringstream(类型安全,适配所有类型)这是最通用、最安全的方式,支持任意数据类型拼接,也是 C++ 最佳实践: 1234567891011121314151617181920212223242526272829303132#include <iostream>#include <sstream>#include <string>using namespace std;// 通用拼接函数:支持任意数量、任意类型的参数template <typename... Args>string concat(Args&&... args) { ostringstream oss; // 折叠表达式(C++17及以上),依次写入所有参数 (oss << ... << args); return oss....
4.文件IO操作
文件IO操作 1. 核心头文件与流对象C++ 实现文件读写必须包含 <fstream> 头文件,该头文件提供了 3 个核心的文件流类,用于不同的文件操作场景: 流类名 功能说明 核心操作方向 ifstream 专门用于从文件读取数据 输入(读) ofstream 专门用于向文件写入数据 输出(写) fstream 既可读又可写文件 输入 + 输出 2. 文件打开模式文件打开时需要指定模式,决定文件的操作规则,可通过 open() 函数或流对象构造函数指定,多个模式用 | 分隔: 模式常量 功能说明 ios::in 以只读方式打开文件;若文件不存在,打开失败 ios::out 以只写方式打开文件;若文件不存在则自动创建,若文件已存在则清空原有内容 ios::app 追加模式;写入的所有数据都会添加到文件末尾,不会清空原有内容 ios::trunc 截断模式;若文件已存在,打开时清空所有内容(ios::out 模式默认包含此特性) ios::binary 二进制模式;按字节流读写(默认是文本模式,会处理换行符等特殊字符)...
3.左值和右值
左值和右值左值(lvalue)和右值(rvalue)是 C++ 中表达式的两种核心分类,其划分依据是表达式是否可以被取地址、是否有持久的存储位置。这个概念是理解 C++ 移动语义、完美转发等高级特性的基础。 一、核心定义与判断标准1. 左值(lvalue, left value) 核心特征: 有持久的存储地址(可以是栈、堆、全局 / 静态存储区); 可以用 & 取地址; 通常出现在赋值语句的左边(但不是绝对标准)。 常见例子: 变量名、数组元素、函数返回的左值引用(T&); *ptr(解引用指针)、a[i](数组下标访问)、this 指针。 123int a = 10; // a 是左值(有地址,可被取址)int* p = &a; // 合法:&a 取左值的地址a = 20; // 合法:左值可以出现在赋值语句左边 2. 右值(rvalue, right value) 核心特征: 没有持久的存储地址(通常是临时对象、字面量,用完即销毁); 不能用 & 直接取地址; 通常出现在赋值语句的右边。 常见例子: 字...
2.万能引用和完美转发
一、万能引用1. 万能引用(Universal Reference)定义:万能引用不是一种新的引用类型,而是 C++11 中对特定形式的引用的称呼 —— 只有当模板参数是 T&& 且 T 是被推导的模板参数时,这个引用才是万能引用(也叫转发引用)。 关键特征: 形式必须是 T&&(模板参数的右值引用形式) 必须发生类型推导(比如模板函数的参数) 能绑定到左值、右值、const/non-const 等所有类型 反例(不是万能引用): 123456// 1. 没有模板参数推导,只是普通右值引用void func(int&& x) {} // 2. 形式不是 T&&(是 const T&&)template <typename T>void func(const T&& x) {} 正例(万能引用): 12template <typename T>void func(T&& x) {&...
1.可调用对象function类
可调用对象function类函数指针用于指向普通成员函数和静态成员函数 定义与使用 定义一个普通函数 1int func(int, int); 定义函数指针类型的变量 1int (*funcPtr)(int, int); //指针通常用ptr表示 使用函数指针的实例: 1234567891011121314151617181920#include<iostream>//定义函数指针类型的变量int (*funcPtr)(int, int);//定义一个普通函数int add(int a, int b){ return a + b;}int main(){ //函数指针指向函数 funcPtr = add; //也可以写funcPtr = &add;函数名等于函数的地址 //调用函数指针指向的函数 funcPtr(1, 2); //不用写成(*funcPtr)(1, 2),编译器会自动解引用,两种写法效果一样 return 0;} 优点与局限性优点: 简单直观,适用于简单的回调函数^回调函数。 局限性: 不能捕获上...