内置类型与运算符相关头文件的包含时机及强制场景问询
这是个特别实用的问题——很多C开发者都会默认觉得「内置类型、运算符肯定不用特意包含头文件」,但实际上C标准里有明确的强制包含场景,咱们一个个拆解你提到的情况:
1. new/delete 与 <new>
普通场景比如int* p = new int;确实不用手动包含<new>——因为很多常用头文件(像<iostream>)会间接引入它。但以下情况必须显式包含<new>:
- 重载全局的
operator new或operator delete函数; - 使用
std::nothrow版本的分配器(比如new(std::nothrow) int); - 捕获
std::bad_alloc异常; - 显式引用
std::new_handler类型或相关函数。
2. typeid 与 <typeinfo>
用typeid做简单的类型对比(比如typeid(a) == typeid(b))时,不少编译器能“蒙混过关”不用<typeinfo>,但标准明确要求:只要你需要访问std::type_info的成员(比如调用name()方法:typeid(int).name()),或者显式声明std::type_info类型的变量/引用,就必须包含<typeinfo>,否则属于未定义行为。
3. std::initializer_list 与 <initializer_list>
像auto l = {1,2,3};或者范围for里写for (int i : {1,2,3}) {}这种隐式生成std::initializer_list的场景,编译器会自动处理,但如果要直接使用这个类型(比如写构造函数接受std::initializer_list<int>参数,或者声明std::initializer_list<int>变量),或者调用它的成员方法(比如l.size()),就必须包含<initializer_list>。
4. std::size_t/std::nullptr_t/std::ptrdiff_t 与 <cstddef>
用sizeof得到的结果会自动转成std::size_t,decltype(nullptr)能拿到std::nullptr_t,指针相减的decltype能得到std::ptrdiff_t——但只要你需要显式声明这些类型的变量、函数参数/返回值,或者直接用它们的类型名,就必须包含<cstddef>(注:std::size_t也可能在<cstdio>、<cstring>等头文件里,但标准规定<cstddef>是这些类型的官方主头文件)。比如写std::size_t len = strlen("test");,没包含对应头文件的话编译器大概率报错。
5. 结构化绑定与 <tuple>
对于简单的结构化绑定(比如绑定数组、普通结构体的public成员),完全不用<tuple>。但如果要绑定std::tuple、std::pair,或者自定义的tuple-like类型(需要支持std::get等操作),就必须包含<tuple>——因为编译器需要这个头文件里的模板支持来完成解构逻辑。
核心总结
一句话:如果只是用运算符隐式生成相关对象/类型,或者只用到运算符的基础功能,可能不需要显式包含头文件;但一旦要显式引用对应的标准库类型名、调用其成员方法,或者使用该类型的进阶功能时,就必须严格包含对应的头文件。
内容的提问来源于stack exchange,提问作者Tomilov Anatoliy

