目录
[1.C++ 聚合初始化(Aggregate Initialization)](#1.C++ 聚合初始化(Aggregate Initialization))
[2.类模板实参自动推导 CTAD](#2.类模板实参自动推导 CTAD)
[2.2.自定义推导指引(Deduction Guide)](#2.2.自定义推导指引(Deduction Guide))
[2.3.聚合类 CTAD(无构造函数,花括号初始化)](#2.3.聚合类 CTAD(无构造函数,花括号初始化))
[2.4.标准库容器 CTAD(最常用场景)](#2.4.标准库容器 CTAD(最常用场景))
[2.5.拷贝 / 移动构造自动推导](#2.5.拷贝 / 移动构造自动推导)
1.C++ 聚合初始化(Aggregate Initialization)
1.1.什么是聚合类
聚合初始化是 C++ 中极简的对象初始化方式 ,专门用于聚合类(Aggregate) ,核心特点:用花括号 {} 直接初始化成员变量,不需要写构造函数。
聚合初始化只给聚合类用 ,一个类 / 数组满足所有以下条件,就是聚合类(C++17 标准):
- 普通数组(天然是聚合);
- 类 / 结构体 / 联合体:
- 没有用户自定义的构造函数;
- 所有非静态成员都是 public(无私有 / 保护);
- 没有虚函数;
- 没有继承基类(C++20 放宽规则);
- 允许默认成员初始值(C++17 新支持)。
✅ 最简单判断:纯数据结构体、数组,无构造 / 无私有 / 无继承 → 就是聚合类。
1.2.聚合初始化核心语法
直接用 { 初始值列表 } 初始化,语法极简:
cpp
聚合类对象 {值1, 值2, 值3...};
// 或(C++11 起 = 可省略)
聚合类对象 = {值1, 值2, 值3...};
1.3.常见用法示例
1.基础结构体(最常用)
纯数据结构体,直接用 {} 赋值,按成员声明顺序初始化:
cpp
#include <string>
// 聚合类:无构造、全public、无继承/虚函数
struct Student {
int id;
std::string name;
double score;
};
int main() {
// 聚合初始化:按声明顺序赋值
Student s1 {101, "张三", 95.5};
// C++11 前写法(= 可省略)
Student s2 = {102, "李四", 88.0};
}
2.数组(天然聚合)
数组是天生的聚合类,花括号初始化是标准用法:
cpp
int arr[] {1, 2, 3, 4}; // 聚合初始化
std::string[] strs {"hello", "cpp"};
3.缺省值初始化(少传参数)
如果只传部分值,剩余成员自动零初始化(数值 = 0,字符串 = 空):
cpp
Student s3 {103};
// 等价:id=103, name="", score=0.0
4.嵌套聚合初始化
聚合类里包含聚合类,嵌套花括号即可:
cpp
struct Point { int x; int y; }; // 聚合类
struct Line { Point start; Point end; }; // 聚合类
int main() {
// 嵌套聚合初始化
Line l {{0,0}, {100, 100}};
}
2.类模板实参自动推导 CTAD
全称 Class Template Argument Deduction ,核心作用:创建模板类对象时,无需显式指定模板参数,编译器根据构造函数实参自动推导类模板参数。
2.1.基础默认推导(编译器自动匹配)
只要构造函数的参数类型和类模板参数能对应,C++17 可直接省略尖括号。
cpp
#include <iostream>
// 模板类定义
template<typename T>
struct Wrapper {
Wrapper(T val) : data(val) {}
T data;
};
int main() {
// C++17 之前必须写 Wrapper<int> w(10);
// C++17 CTAD 自动推导 T = int
Wrapper w(10);
// 多模板参数同理
template<typename T1, typename T2>
struct Pair {
Pair(T1 a, T2 b) : first(a), second(b) {}
T1 first;
T2 second;
};
Pair p(1, 3.14); // 推导 Pair<int, double>
return 0;
}
2.2.自定义推导指引(Deduction Guide)
当默认推导规则不满足需求(构造函数参数与模板参数不直接对应、存在歧义、需要强制转换),需要手动写推导指引。
语法格式:
cpp
template<模板参数列表>
类名(构造参数类型列表) -> 类名<推导后的模板参数>;
如:
cpp
#include <string>
template<typename T>
struct Box {
// 构造函数接收 const T&,默认推导会出现歧义
Box(const T& t) : val(t) {}
T val;
};
// 自定义推导指引:强制根据实参推导 T
template<typename T>
Box(T) -> Box<T>;
// 推导指引:强制让字面量推导为对应类型
Box(const char*) -> Box<std::string>;
int main() {
Box b("hello"); // 推导 Box<std::string>(无指引会推 const char*)
Box b2(std::string{"hi"}); // 推导 Box<std::string>
return 0;
}
2.3.聚合类 CTAD(无构造函数,花括号初始化)
没有自定义构造函数的聚合类 (纯成员变量),用 {} 初始化也能自动推导。
cpp
template<typename T>
struct Data { // 聚合类:无构造、无私有成员、无虚函数
T value;
int id;
};
int main() {
Data d{3.14, 100}; // 推导 T=double → Data<double>
}
2.4.标准库容器 CTAD(最常用场景)
C++17 对 STL 容器、智能指针、pair/tuple 都内置了 CTAD 支持,大幅简化代码:
cpp
#include <vector>
#include <pair>
#include <tuple>
#include <string>
int main() {
std::vector v{1, 2, 3}; // vector<int>
std::pair p(10, std::string{"a"}); // pair<int, std::string>
std::tuple t(1, 2.0, 'c'); // tuple<int, double, char>
return 0;
}
2.5.拷贝 / 移动构造自动推导
编译器对拷贝、移动构造自动支持 CTAD,无需手动写推导指引。
cpp
MyClass a(10);
MyClass b = a; // 自动推导 T=int,完美支持
3.注意事项
1.禁止「部分显式 + 部分自动推导」
要么全写模板参数 ,要么全推导,不能只写一部分!
cpp
template<typename T, typename U>
struct Pair { Pair(T a, U b) {} };
int main() {
Pair<int> p(1, 2.0); // ❌ 错误!不能只指定部分参数
Pair<int, double> p1(1,2.0); // ✅ 全显式
Pair p2(1,2.0); // ✅ 全推导
}
2.模板别名(using)不支持 CTAD
用 using 定义的模板别名 ,无法使用自动推导,必须显式指定类型。
cpp
#include <vector>
template<typename T>
using MyVec = std::vector<T>;
int main() {
MyVec v{1,2,3}; // ❌ 错误!模板别名不支持CTAD
MyVec<int> v{1,2,3};// ✅ 必须显式写类型
}
3.不能用于「静态成员、单独声明」
CTAD 只能用于创建对象,声明变量、静态成员必须显式指定类型。
cpp
template<typename T> struct A {};
A<int> a; // ✅ 声明必须显式
A a2; // ❌ 错误,仅创建对象时可推导