C++17之类模板实参自动推导CTAD

目录

[1.C++ 聚合初始化(Aggregate Initialization)](#1.C++ 聚合初始化(Aggregate Initialization))

1.1.什么是聚合类

1.2.聚合初始化核心语法

1.3.常见用法示例

[2.类模板实参自动推导 CTAD](#2.类模板实参自动推导 CTAD)

2.1.基础默认推导(编译器自动匹配)

[2.2.自定义推导指引(Deduction Guide)](#2.2.自定义推导指引(Deduction Guide))

[2.3.聚合类 CTAD(无构造函数,花括号初始化)](#2.3.聚合类 CTAD(无构造函数,花括号初始化))

[2.4.标准库容器 CTAD(最常用场景)](#2.4.标准库容器 CTAD(最常用场景))

[2.5.拷贝 / 移动构造自动推导](#2.5.拷贝 / 移动构造自动推导)

3.注意事项


1.C++ 聚合初始化(Aggregate Initialization)

1.1.什么是聚合类

聚合初始化是 C++ 中极简的对象初始化方式 ,专门用于聚合类(Aggregate) ,核心特点:用花括号 {} 直接初始化成员变量,不需要写构造函数。

C/C++中{}的用法总结(全)
C++之std::initializer_list详解

聚合初始化只给聚合类用 ,一个类 / 数组满足所有以下条件,就是聚合类(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;       // ❌ 错误,仅创建对象时可推导
相关推荐
众少成多积小致巨13 小时前
JNI (Java Native Interface) 技术手册中文参考指南
android·java·c++
clint4565 天前
C++进阶(1)——前景提要
c++
夜悊5 天前
C++代码示例:进制数简单生成工具
c++
郝学胜_神的一滴5 天前
CMake 021: IF 条件判据详诠
c++·cmake
_wyt0015 天前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
LDR0065 天前
Type-C 快充全面升级!LDR6601 赋能个人护理便携电机,重塑剃须刀 / 理发器新体验
c语言·开发语言
雪碧聊技术5 天前
Tree.js是什么?一文讲透
开发语言·javascript·ecmascript
码云数智-园园6 天前
C++20 Modules 模块详解
java·开发语言·spring
swordbob6 天前
NIO的channel中什么是 fd(File Descriptor,文件描述符)
java·开发语言·nio
源分享6 天前
Java线程同步的多种实现方法(非常详细)
java·开发语言·jvm