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;       // ❌ 错误,仅创建对象时可推导
相关推荐
iCxhust1 小时前
C# 程序,实现二进制文件十六进制查看器,支持按行定位
开发语言·单片机·嵌入式硬件·c#·微机原理·8086最小系统·8088单板机
咕噜企业签名分发-淼淼1 小时前
浅谈云服务器在后端托管与签名分发场景中的应用价值
开发语言·php
在繁华处1 小时前
Java从零到熟练(八):泛型与注解
java·开发语言·python
SilentSamsara1 小时前
命令行工具开发:Click/Typer + 打包为独立二进制
linux·服务器·开发语言·前端·python·青少年编程·fastapi
Ulyanov1 小时前
深入QML滑块与进度控制:构建动态数据可视化界面:QML+PySide6现代开发入门(六)
开发语言·python·算法·ui·信息可视化·雷达电子对抗仿真
星马梦缘1 小时前
ACM笔记 学习版本
数据结构·c++·算法
zyl837211 小时前
Python 函数、模块、异常处理 超详细入门教程
开发语言·windows·python
苏州IT威翰德1 小时前
苏州IT基础架构IQ/OQ/PQ确认服务 | 服务器网络验证
开发语言·php
Xin_ye100861 小时前
C# 零基础到精通教程 - WPF 专题二:数据绑定与 MVVM
开发语言·c#·wpf