Browse Source

feat: 添加非类型模板参数

liucong5 7 months ago
parent
commit
1f56d8b662
1 changed files with 158 additions and 1 deletions
  1. 158 1
      cpp/模板/README.md

+ 158 - 1
cpp/模板/README.md

@@ -691,6 +691,163 @@ int main() {
 
 ### 聚合类的模板化
 
-**聚合类** 指的是 用户没有定义的显式的或者继承而来的构造函数,没有 private、protected 的静态成员,没有虚函数,没有 virtual、private或者protected 的基类
+**聚合类** 指的是 用户没有定义的显式的或者继承而来的构造函数,没有 `private`、`protected` 的非静态成员,没有虚函数,没有 `virtual`、`private` 或者 `protected` 的基类
+
+```cpp
+template<typename T>
+struct ValueWithComment {
+    T value;
+    std::string comment;
+}
+
+ValueWithComment<int> vc;
+vc.value = 42;
+vc.comment = "initial value";
+
+ValueWithComment(char const*, char const*)-> ValueWithComment<std::string>;
+
+ValueWithComment vc2 = {"hello", "initial value"};
+```
+
+没有**推断指引**的话,就不能使用上述初始化方法,因为`ValueWithComment` 没有相应的构造函数来完成相关类型推断
+
+## 非类型模板参数
+
+模板参数不一定非得是某种具体的类型,也可以是**常规数值**
+
+与类模板使用类型作为参数类似,可以使代码的另一些细节留到被使用时再确定
+
+```cpp
+template<typename T, std::size_t MaxSize>
+class Stack{
+public:
+    T x[MaxSize];
+};
+
+int main() {
+    Stack<int, 10> a;
+    std::cout << sizeof(a.x) << std::endl;  // Log: 40 
+    return 0;
+}
+```
+
+> 输出 40,一个 int 占 4 字节,10个元素就是 4 * 10 = 40 字节
+
+```cpp
+template<int Val, typename T>
+T addValue (T x)
+{
+    return x + Val;
+}
+
+int main() {
+    std::cout << addValue<10>(2) << std::endl;  // log: 12
+    return 0;
+}
+```
+
+除了常规定义之外,还可以做一些其他的事情,比如下面当作仿函数使用(不是真的仿函数)
+
+```cpp
+std::transform (
+    source.begin(), 
+    source.end(),
+    dest.begin(), 
+    addValue<5,int>)
+```
+
+> 这里 `addValue` 被实例化为 传入int 型参数加 5 的函数实例
+
+也可以像下面这样操作,通过传入的 `Val` 推导出结果
+
+```cpp
+template<auto Val, typename T = decltype(Val)>
+T foo();
+```
+
+### 非类型模板参数的限制
+
+通常只能是
+
+- 整型常量(包括枚举)
+- 指向 objects/functions/members 的指针
+- objects 或 functions 的左值引用
+- std::nullptr_t 
+
+```cpp
+// 整型常量
+template<int Size>
+struct FixedArray {};
+
+FixedArray<5> arr5;
+
+// 枚举常量
+enum class Color { Red, Green, Blue };
+template<Color C>
+struct TrafficLight {};
+
+TrafficLight<Color::Red> redLight;
+
+// 一些定义 方便后面使用
+int global_value = 42;
+void print_hello() {}
+struct MyClass {
+    int value = 100;
+    void print() { 
+        std::cout << "MyClass::print()\n"; 
+    }
+};
+
+// 指向对象的指针
+template<int* Ptr>
+struct PointerWrapper {};
+
+PointerWrapper<&global_value> pw;
+
+// 指向函数的指针
+template<void (*FuncPtr)()>
+struct FunctionWrapper {};
+
+FunctionWrapper<&print_hello> fw;
+
+// 指向成员对象的指针
+template<int MyClass::*MemberPtr>
+struct MemberWrapper {};
+
+MemberWrapper<&MyClass::value> mw;
+
+// 指向成员函数的指针
+template<void (MyClass::*MemberFuncPtr)()>
+struct MemberFunctionWrapper {};
+
+MemberFunctionWrapper<&MyClass::print> mfw;
+```
+
+**浮点型数值**或者 class 类型的对象都不能作为非类型模板参数使用
+
+```cpp
+template<double VAT>    // Error
+double process (double v) 
+{
+    return v * VAT;
+}
+template<std::string name> // ERROR:
+class MyClass {};
+```
+
+**非类型模板参数**支持**编译期表达式**
+
+```cpp
+C<sizeof(int) + 4, sizeof(int)==4> c
+```
+
+但是如果涉及到 `operator >` 计算的时候要小心,容易被解析成类型模板的 反尖括号
+
+```cpp
+C<sizeof(int) + 4, sizeof(int) > 4> c; // Error 第一个 > 符号被理解成模板的反尖括号结尾,导致解析错误
+C<sizeof(int) + 4, (sizeof(int) > 4)> c; // OK 用括号抱起来
+```
+
+## 变参模板