尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

【C++ 面试真题】34. 聊聊 C++ 的类型特征( type_traits)

【C++ 面试真题】34. 聊聊 C++ 的类型特征( type_traits) 【C 面试真题】聊聊 C 的 type_traits“这个模板参数到底是不是指针”“把 T 的引用剥掉是什么类型”——泛型代码里你需要在编译期问类型问题、对类型做变换这就是type_traits的领地。背得出is_integral只是及格真考你的是三类 traits 分别干什么、_v 和 _t 后缀怎么来的、decay 是干嘛的、怎么手写一个 trait。本文把类型特征一次讲透。一、开场type_traits 是什么❓ 介绍一下type_traits✅ 一套**编译期的类型属性查询与类型变换**工具[C11]进标准库。按功能分三类类别干什么代表一元特征查属性 → boolis_pointer、is_integral二元特征查关系 → boolis_same、is_base_of类型变换变类型 → ::typeremove_reference、decay组合按条件选conditional、enable_if// 问类型问题is_pointerint*::value;// trueis_sameint,int32_t::value;// 变换类型remove_referenceint::type t;// int回答思路先报查询、变换、组合三件事点一句返回值是编译期常量、服务于 if constexpr 和 enable_if——这就把用法主线讲清了。二、查询类属性与关系❓ 常用的查询类 traits 有哪些✅ 两族高频清单属性族一元is_integral_vT// 整型家族is_floating_point_vTis_pointer_vT// 指针is_class_vT// 类/结构体is_empty_vT// 空类is_trivially_copyable_vT// 可 memcpy关系族二元is_same_vT,U// 同一类型is_base_of_vBase,D// 继承关系is_convertible_vFrom,To⚠️is_same是严格同一is_same_vint, const int是false——const、引用、volatile 任何一点不同都算不同类型。想宽松比较先变换再比下一节的 decay/remove_const。三、变换类剥修饰、造新类型❓ 类型变换 traits 都干什么✅ 把类型的修饰层层剥掉/加回去::type是产物remove_referenceint::type// intremove_constconstint::type// intremove_pointerint*::type// intadd_pointerint::type// int*make_signedunsigned::type// int最常用也最容易被低估的是decay——“像按值传参一样退化”decayconstint::type// intdecayint[10]::type// int*数组退化为指针decayint(double)::type// int(*)(double)函数退化为// 函数指针decay 的定义就是函数模板按值传参时的类型推导规则——所以它出现在一切模拟按值传参的场合std::thread存参数、std::bind存实参、tuple 的构造内部全靠 decay 归一化。面试能讲出decay 传参退化说明你理解它不是随手剥修饰。四、后缀便捷版_v 与 _t❓ ::value、::type 之外为什么还有 _v、_t✅ 纯粹的语法糖少打四个字符// C11 原版is_sameint,int::value remove_referenceint::type// C14/17 变量模板 别名模板版is_same_vint,int// [C17]remove_reference_tint// [C14]实现就一行变量模板——上期讲过的四类模板之一templateclassTinlineconstexprboolis_pointer_vis_pointerT::value;新代码一律_v/_t——可读性完胜尤其嵌套时remove_reference_tdecay_tT对比原版的长串::type::type。五、组合类conditional 与 enable_if❓ 按条件选类型或启停函数用什么✅ 两个组合器是 trait 和模板编程的接口层conditional——编译期三目// 大对象用引用、小对象用值usingTconditional_t(sizeof(Big)16),constBig,Big;enable_if——“条件不满足就让这个重载消失”templateclassT,enable_if_tis_integral_vT,int0voidonlyInt(T x);// T 是整型才有这个函数// 浮点调用 → 编译错误或// 落到别的重载它和 SFINAE 的关系密切到常被当成同义词——细节留给下下篇专题。这里记住分工conditional 选类型enable_if 选函数。 和if constexpr的分工if constexpr 在函数体内分支两支都编译、一支丢弃enable_if 在重载决议层取舍整个函数存或不存在。前者组织代码后者筛选接口。六、手写一个 trait理解原理❓ traits 的原理是什么自己写一个✅ 核心模板特化 一个编译期常量成员。手写is_pointer玩具版// 泛化版默认不是指针templateclassTstructIsPointer{staticconstexprboolvaluefalse;};// 偏特化T 是指针时命中templateclassTstructIsPointerT*{staticconstexprboolvaluetrue;};IsPointerint::value;// falseIsPointerint*::value;// true一句话原理泛化模板给默认答案偏特化给特定模式的专属答案——编译器最特化优先的匹配规则替你完成了 if-else。标准库所有查询类 traits 都是这个模式真实实现基于 integral_constant 继承附带 ::type 等元函数约定。 探测有没有某成员的进阶玩法是void_t 技巧——几行代码让has_reserveContainer::value自动成立或失败。原理是表达式合法则特化存在是 SFINAE 的经典应用下下篇展开。七、面试高频追问❓ Q1integral_constant 是什么✅ 编译期布尔值的基类——integral_constantbool, true携带常量value和类型type指向自己。所有is_xxx都继承它因此除了::value还支持operator bool、继承::type让 trait 能当编译期值参与元编程true_type/false_type就是它的两个别名。❓ Q2type_traits 在标准库哪里被用到✅ 无处不在vectorbool的实现、min/max的参数检查move/forward内部就是remove_reference_tT下期展开容器的swap用is_trivially_copyable选 memcpy 路径thread/bind 用decay存参数。可以说标准库的泛型代码一半的聪明都建立在 traits 之上。❓ Q3is_convertible 和 is_same 什么时候结论不同✅is_same_vdouble, intfalse但is_convertible_vint, doubletrue——前者问同一类型身份后者问能否隐式转换能力。检查可隐式转成字符串这类宽松契约用前者会全盘误杀。❓ Q4为什么 decay 重要举一个真实场景✅thread的构造函数按值存参数传int arr[10]进去存的是int*传const string存的是string拷贝——这正符合用户对参数自带一份的预期。实现上就是decay_tArgs...的成员声明。没有 decay数组/函数类型直接没法当成员存。❓ Q5trait 能在运行期用吗✅ 能但没意义——if (is_pointer_vT)编译期就把值定了运行期判断恒真/恒假还会让两个分支都实例化可能编译报错。类型问题就该编译期解决if constexpr 或 enable_if运行期 if 是误用。❓ Q6C20 concepts 会取代 type_traits 吗✅ 不会取代而是接管它最难的用法。查询/变换is_pointer、decay照旧用约束模板参数从 enable_if 的黑魔法变成 concepts 的声明式语法requires integral T——traits 是砖concepts 是漂亮的门面两者配合下下篇见。❓ Q7decay 和 remove_reference 什么区别✅ remove_reference 只剥一层引用decay 是全套退化——剥引用之外还去 const/volatile 顶层修饰、数组退化为指针、函数退化为函数指针。decay_tconst char[8]是const char*remove_reference_t做不到这步。模拟按值传参用 decay只想去引用才用 remove_reference——两者差的不止一个修饰符。名字也是钩子decay 的本义就是 C 数组传参的退化数组、函数的退化只有它做。八、总结速查表考点一句话结论三件事查属性、变类型、组合选择查询is_pointer_v / is_same_vis_same严格同一const 不容变换remove_reference_t / decay_tdecay模拟按值传参的退化后缀_v [C17] / _t [C14]conditional编译期三目选类型enable_if条件不满足函数消失手写原理泛化默认值 偏特化覆盖与 if constexpr体内分支 vs 接口筛选一句话回顾type_traits干三件事查is_pointer_v、变decay_t、组conditional/enable_if手写原理就是泛化默认 偏特化覆盖新代码全用_v/_t后缀decay 按值传参的退化规则decay 之外的组合conditional 选类型、enable_if 选函数——它们共同构成下期 move/forward 与库设计的地基。如果您觉得本篇内容对你有帮助欢迎点赞 、收藏 ⭐、转发 。下期我们继续模板元篇——聊 std::move/std::forward 与库设计完美转发在 emplace、工厂、包装器这些架构里是怎么用的敬请关注
返回列表