%% # 纲要 > 主干纲要、Hint/线索/路标 # Q&A #### 已明确 #### 待明确 > 当下仍存有的疑惑 **❓<font color="#c0504d"> 有什么问题?</font>** # Buffer ## 闪念 > sudden idea ## 候选资料 > Read it later %% # 模版元编程 模板元编程(_template metaprogramming_,TMP) # Type Traits 头文件 C++ 标准库头文件 `<type_traits>` 中提供了一系列用于进行 "**==类型处理==**"(**类型转换**、**类型查询**) 的**类模版**, 这些类提供了 "**在编译时==对类型进行查询和转换==**" 的功能,通常用于 "**模版元编程**" 设计中[^2]。 ![image-20231018092130602](_attachment/02-开发笔记/01-cpp/模版与泛型编程/cpp-type_traits%20头文件.assets/IMG-cpp-type_traits%20头文件-C62684F46C26B04F2874A81945526849.png) <br><br> # type_traits 中模版的实现说明 ### 类型转换模版的实现 示例一:`std::remove_reference<>` 等类模版的实现:通过 "**==struct"类型成员==** & "**==模版特例化==**" 进行实现[^3]. ```cpp // std::remove_reference<>的实现方式:通过"struct"类型成员 & "模版特例化" 进行实现. namespace MySpace { template <typename T> struct remove_reference { typedef T type; }; template <typename T> struct remove_reference<T&> { // 针对左值引用的 "模版特例化" 版本 typedef T type; }; template <typename T> struct remove_reference<T&&> { // 针对右值引用的 "模版特例化" 版本 typedef T type; }; } // end namespace MySpace int main() { MySpace::remove_reference<int&>::type var = 25; // var是int类型. return 0; } ``` 示例二:`std::remove_reference_t<>` 等带 `_t` 后缀的实现:通过定义 "**==别名模版==**" 得到 ![[_attachment/02-开发笔记/01-cpp/模版与泛型编程/cpp-type_traits 头文件.assets/IMG-cpp-type_traits 头文件-0C7EC4D51FF1C2D0DE3C75C157465FC0.png|566]] ## 类型判断模版的实现 略。 - 原始版本,例如 `std::is_pointer<T>` 暂时不太能理解,似乎涉及到**编译器层面的判断**。 - `_v` 后缀版本,是定义的 "**变量模版**",例如 `std::is_pointer_v<T>` 是关于 `std::is_pointer<T>::value` 的变量模版。 # "类型转换" 模版 主要的**类型转换** 相关模版: ![[_attachment/02-开发笔记/01-cpp/模版与泛型编程/cpp-type_traits 头文件.assets/IMG-cpp-type_traits 头文件-3879CB28E522824038E1F5C0D24CF57E.png|658]] 这些模版类都 **接受一个模版参数 `T`**,而**给出==转换所得的另一类型==**。 当**无法进行类型转换**时,给出的将是 `T` 本身。 在具体用法上,有两种方式: > [!NOTE] 关于 `<type_traits>` 中 "类型转换" 相关接口的使用说明 > > `<type_traist>` 所提供的所有 "**==类型转换==**" 相关的类模版,有两种版本 [^1], > > - (1)**无 `_t` 后缀**的版本,其**具有一个公有成员 `type`**,由该成员给出对应类型; > - 其实现是通过 **`struct` 模版内嵌套 `typedef`** 完成的,故必须通过其成员 `::type` 使用,且**需要由关键字 `typename` 明确指出为类型名**。 > > > > > - (2)**带 `_t` 后缀**的版本(since C++14),其**本身直接给出对应类型**。 > - 其实现直接通过 "**==别名模版==**" 完成,故可直接使用,不再需要 `::type`。 > > > ![[_attachment/02-开发笔记/01-cpp/模版与泛型编程/cpp-type_traits 头文件.assets/IMG-cpp-type_traits 头文件-FD71E765C9C603309CA63B219289CE89.png|626]] > > ^1ecwlu > <br> ### CV 限定相关 `std::remove_const<>`,`std::remove_volatile<>`,`std::remove_cv<>` : 这些模板用于**移除类型的 `const`、`volatile` 修饰符**,或同时移除两者(`cv` 表示 `const-volatile`)。 ```cpp using T = const int; using U = std::remove_const<T>::type; // U 是 int ``` `std::add_const`,`std::add_volatile`,`std::add_cv`: 这些模板用于**给类型添加 `const`、`volatile` 修饰符**,或同时添加两者。 ```cpp using T = int; using U = std::add_const<T>::type; // U 是 const int ``` <br> ### 引用相关 `std::remove_reference<>`,`std::add_l_value_referece<>`,`std::add_rvalue_reference<>`: 这些模板用于移除类型的引用修饰符,或添加左值引用或右值引用。 ```cpp using T = int&; using U = std::remove_reference<T>::type; // U 是 int using V = std::add_rvalue_reference<U>::type; // V 是 int&& ``` ### 指针相关 - `std::add_pointer<>`:用于将类型 `T` 转换对应指针类型 `T*` - `std::remove_pointer<>`:用于将指针类型 `T*` 转为所指类型 `T` ```cpp using U = std::add_pointer<int>::type; // U是int* using V = std::remove_pointer<int*>::type; // U是int ``` ### 获取衰变类型 `std::decay` 用于将类型“衰变”为它们**在函数传递中会被转换为的类型**,包括**移除引用、移除 `cv` 限定符、将数组转换为指针、将函数转换为函数指针**。 ### 获取底层类型 对于**枚举类型**,`std::underlying_type` 用于获取其底层整型类型 ```cpp enum class MyEnum : unsigned int {}; using T = std::underlying_type<MyEnum>::type; // T 是 unsigned int ``` <br><br><br> # "类型判断" 模版 > [!NOTE] 关于 `<type_traits>` 中各个接口的使用说明 > > `<type_traist>` 所提供的所有 "**==类型判断==**" 相关的类模版,有两种版本 [^1], > > - (1)**无 `_v` 后缀**的版本,其**具有一个公有成员 `value`(`bool` 类型)**,**由该成员变量给出判断结果**。 > - (2)**带 `_v` 后缀**的版本(since C++14),其**本身是一个 "==变量模版=="**,**故可直接使用**。 > - 如图所示,在 `<type_traits>` 中的定义如下: ![[_attachment/02-开发笔记/01-cpp/模版与泛型编程/cpp-type_traits 头文件.assets/IMG-cpp-type_traits 头文件-BCFEBB80D51C82B32ECDA456C420BF05.png|503]] > 说明示例: ```cpp // 判断是否为整型 std::is_integral<T>::value // bool值 std::is_integral_v<T> // 与上等价, 为bool类型的变量模版. since C++14 // 判断是否为指针 std::is_pointer<T>::value // bool值 std::is_pointer_v<T> // 与上等价, 为bool类型的变量模版. since C++14 ``` <br><br><br> # 用途示例 ### 灵活重载 示例一:为整数类型 (short, int, long long 等) 和非整型 (float, double) 实现不同的函数 ```cpp #include <type_traits> using namespace std; // 根据<type_traits>中提供的std::is_integral<T>()来判断类型, 为整数型和浮点型调用不同的函数 // 使用type_traits的好处在于: // 对所有整型(包括short, int, long long)和非整型(float, double)只需要实现两个函数即可. template <typename T> void fool_impl(T val, true_type); // provide integral version template <typename T> void fool_impl(T val, false_type); // provide floating-point version. template <typename T> void foo(T val) { fool_impl(val, std::is_integral<T>()); } ``` 示例二:为任意类型,两个版本。 ```cpp #include <type_traits> using namespace std; template <typename T> void foo_impl(const T &val, std::true_type); template <typename T> void foo_impl(const T& val, std::false_type); template <typename T> void foo(const T &val) { // 根据模版实例化时T的类型, 来调用对应的 // `std::is_pointer<T>()` 根据T的类型返回type `std::true_type` 或 `std::false_type` foo_impl(val, std::is_pointer<T>()); } // 上述实现等价于如下实现: template <typename T> void foo (const T& val); // 针对寻常type template <typename T> void foo<T*> (const T& val); // 针对pointer type ``` <br><br> ## 处理共通类型 共通类型(Common Type):可用于处理两种不同类型的值的类型. 例如,求两个不同类型的值的和或者最小值,就需要以共通类型作为返回值。 示例一:返回两个不同类型中的最小值 ```cpp #include <type_traits> // 使用std::common_type<>来得到T1和T2的共通类型, 作为返回类型 template <typename T1, typename T2> typename std::common_type<T1,T2>::type min(const T1 &x, const T2 &y); // 如果T1和T2都是int或者long, 或者一个int另一个long, 则std::common_type<T1,T2>返回int. // 如果其一是string, 而另一个是const char*, 则返回结果是string. ``` <br><br> # ♾️参考资料 # Footnotes [^1]: 《Effective Modern C++》Item9 [^2]: 《C++ Primer》P606 [^3]: 《C++ Primer》P628