@@ -537,6 +537,8 @@ public:
537537
538538C++ 标准库中, RAII 无处不在。
539539
540+ 需要注意的是,资源管理不止是内存资源,也包括非内存资源比如说文件读写资源等等。
541+
540542### 操作符重载
541543
542544* 好像也没什么好记的。*
@@ -594,7 +596,7 @@ constexpr complex<double> operator""i(long double arg) {
594596- 用传值的方式返回容器(依赖拷贝消除和移动以提高效率)
595597- 对于容量较大的操作数,用 const 引用作为参数类型。
596598- 避免显式使用 std::copy()
597- - 用 RAII 管理所有资源——内存和非内存资源。
599+ - ** 用 RAII 管理所有资源——内存和非内存资源。**
598600- 如果将类型的 <=> 定义为非默认值,那么也要定义 == 操作符
599601- 遵循标准库容器设计
600602
@@ -720,3 +722,162 @@ struct Final_action {
720722finally([ &] {free(p);}) // 离开作用域时自动调用匿名函数,使用 free 释放
721723```
722724
725+ ### 模板机制
726+
727+ 想定义好的模板,我们需要一些支撑性的语言设施。
728+ - 依赖类型的值:模板变量(*这里似乎是笔误,原文说的是参数模板,但是指向的底下标题却是「模板变量」)*
729+ - 类型与模板的别名:别名模板
730+ - 编译时选择机制:`if constexpr`
731+ - 编译时查询值与表达式属性的机制:`requires` 表达式
732+
733+ 除此外,`constexpr 函数` 和 `static_asserts` 也经常出现在模板设计和使用中。
734+
735+ #### 模板变量
736+
737+ 依赖模板类型的常量或变量
738+
739+ ```cpp
740+ template<class T>
741+ constexpr T viscosity = 0.4;
742+
743+ template<class T>
744+ constexpr space_vector<T> external_acceleration = { T{}, T{-9.8}, T{} };
745+
746+ auto vis2 = 2*viscosity<double>;
747+ auto acc = external_acceleration<float>;
748+ ```
749+
750+ * 这是 C++14 出的功能。*
751+
752+ 标准库使用模板变量来提供数学常数,比如说 pi 以及 log2e.
753+
754+ #### 别名
755+
756+ 可以这么起别名。
757+
758+ ``` cpp
759+ using size_t = unsigned int ;
760+ ```
761+
762+ 意思是 ` size_t ` 是 ` unsigned int ` 的别名。
763+
764+ 有参数的模板类型经常针对特定参数提供特定别名。
765+
766+ ``` cpp
767+ template <typename T>
768+ class Vector {
769+ public:
770+ using value_type = T;
771+ // ...
772+ }
773+
774+ template<typename C >
775+ using Value_type = C::value_type;
776+
777+ template<typename Container >
778+ void algo(Container& c) {
779+ Vector<Value_type<Container >> vec;
780+ }
781+ ```
782+
783+ *我查了一下,C++20有适用于 Range 的简略写法。不过讨论回记录类型本身,其实可以省略中间变量,只要 C++11 以上就能用的语法 `Vector<typename Container::value_type> vec;` *
784+
785+ 此外别名也可以通过绑定模板的部分参数来定义新的模板。
786+
787+ ```cpp
788+ template<typename Key, typename Value>
789+ class Map {
790+ // ...
791+ }
792+
793+ template<typename Value>
794+ using String_map = Map<string,Value>
795+
796+ String_map<int> m; // Map<string,int>
797+ ```
798+
799+ #### 编译时 if
800+
801+ 使用编译时 if 让模板决定根据具体类型使用哪种方案。
802+
803+ ``` cpp
804+ void update (T& target) {
805+ // ...
806+ if constexpr(is_trivially_copyable_v<T >)
807+ simple_and_fast(target); // 对于 PDO 类型
808+ else
809+ slow_and_safe(target); // 对于复杂类型
810+ //...
811+ }
812+ ```
813+
814+ `is_trivially_copyable_v<T>` 是一个类型谓词,表明类型是否可以以较小的代价被拷贝。
815+
816+ 编译器只会编译所选择的 if constexpr 分支。这个方案提供了最佳性能以及最佳的局部性。
817+
818+ 此外需要注意的是这不是编译器宏,所以 `if constexpr` 不是文本处理机制。
819+
820+ ### 建议
821+
822+ - 用模板表达那些用于多种参数类型的算法。
823+ - 用模板实现容器。
824+ - **模板是类型安全的,但是对于无约束的模板,检查发生得太晚了。**
825+ - 把函数对象作为算法的参数。
826+ - 如果简单的函数对象只在某处使用一次,不妨使用匿名函数。
827+ - **不能把虚函数成员定义成模板成员函数。**
828+ - 使用 finally() 为不带析构函数且需要「清理操作」的类型提供 RAII;
829+ - 使用 if constexpr 条件编译提供替代实现,不会存在运行时开销。
830+
831+ ## 第8章 概念和泛型编程
832+
833+ *C++20 超新特性。当然对于这本书来说是超新,对于这篇博客来说已经是 5 年前了。*
834+
835+ > 应该把模板用在哪儿呢?换句话说,模板会让哪些程序设计技术更有效呢?模板提供了以下功能:
836+ > - 在不丢失信息的情况下将类型(以及值和模板)作为参数传递的能力。这意味着可以表达的内容具有很大的灵活性以及具有内联的绝佳机会,当前的实现充分利用了这一点。
837+ > - 有机会在实例化时将来自不同上下文的信息捏合在一起,这意味着有进行针对性优化的可能。
838+ > - 把值作为模板参数传递的能力,也就是在编译时计算的能力。
839+ >
840+ > 总而言之,模板为编译时计算和类型控制提供了强有力的机制,使得我们可以编写出更加简洁高效的代码。记住,类可以包含代码和值。
841+
842+ 模板的最常见应用是支持 **泛型编程(generic programming)**,泛型编程主要关注通用的算法设计。
843+
844+ *原来 generic 是指通用,虽然这么说好像在说原来 apple 是苹果一样奇怪,但我还是去想泛型编程的作用而不是当只知道凭直觉用。*
845+
846+ ### 概念
847+
848+ 使用概念来进行模板参数的限定。
849+
850+ 例如 `range_value_t<Seq>` 表示的是序列中的元素类型。`Arithmetic<X,Y>` 则表示 X 和 Y 能进行算术运算。
851+
852+ 概念可以这么使用。
853+
854+ ```cpp
855+ template<Sequence Seq, Number Num>
856+ requires Arithmetric<range_value_t<Seq>, Num>
857+ Num sum(Seq s, Num n);
858+ ```
859+
860+ > ` requires Arithmetric<range_value_t<Seq>, Num> ` 被称作 ` requirements ` 子句。
861+
862+ * 谁发明的「概念」,「requirements子句」这都要起别名吗。*
863+
864+ ` template<Sequence Seq> ` 就是 ` requires Sequence<Seq> ` 更简单的写法。
865+
866+ 不支持 concept 的代码,可以把代码用注释的形式写出来:
867+
868+ ``` cpp
869+ template <typename Seq, typename Num>
870+ // requires Arithmetic<range_value_t<Sequence>,Number>
871+ Num sum (Seq s, Num n);
872+ ```
873+
874+ *难蚌,真就人脑一个编译器了。不过重点还是把模板设计的约束条件写清楚。*
875+
876+ #### 基于概念的重载
877+
878+ 就是用概念不同来重载。编译器会选择满足最严格参数需求的版本。
879+
880+ 跟其他的重载一样,这是编译时机制,没有任何运行时开销,如果编译器找不到最佳选择,就会报告二义性错误。基于概念的重载比一般的重载效率更高。
881+
882+ #### 有效代码
883+
0 commit comments