C++_类模板(二)

发布时间:2026/10/15 19:34:37
C++_类模板(二)
模板型成员类型模板型成员类型又称成员类模板是定义在类模板内部的类模板。它允许类模板中包含一个或多个依赖于不同类型参数的嵌套类模板。一个类模板的成员是另一个类模板templatetypename X class A { public: templatetypename Y class B { // B 是一个依赖于类型参数 Y 的类模板 }; };该语法结构使得B可以独立于外部类模板的实例化而被实例化且其类型参数与外层类模板无关。A类模板只需声明或定义无需实例化即可支持B的定义。使用示例#include iostream using namespace std; templatetypename T class A { public: templatetypename D class B { public: templatetypename R void foo() { cout foo endl; } }; }; // 成员函数模板的定义必须在类外显式写出 templatetypename T templatetypename D templatetypename R void AT::BD::foo() { cout foo endl; } int main(void) { Adouble::Bfloat q; q.fooshort(); return 0; }Adouble实例化后生成一个名为Bfloat的类。q.fooshort()调用的是Bfloat内部的fooshort函数模板实例。所有层级的模板参数必须在调用时明确指定。类模板中的成员虚函数类模板可以包含普通成员虚函数这些虚函数支持多态行为其机制与非模板类一致。特性说明支持虚函数类模板可以定义virtual成员函数。支持多态通过基类指针调用派生类重写的虚函数可实现运行时绑定。不支持虚函数模板不能在类模板中声明virtual templatetypename R void bar();这样的语法。原因分析虚函数表vtable是在类实例化时由编译器构建的。成员函数模板具有延迟实例化特性只有当函数被实际调用时才会生成具体函数体。在创建虚函数表时成员函数模板尚未实例化无法确定其地址。因此编译器不允许将函数模板声明为虚函数。正确写法示例#include iostream using namespace std; templatetypename T class A { public: virtual void foo() { cout AT::foo endl; } // ❌ 错误不能声明虚函数模板 // virtual templatetypename R void bar(); // ✅ 正确普通成员函数 void bar() { cout AT::bar endl; } }; templatetypename T, typename D class B : public AT { public: void foo() override { cout BT,D::foo endl; } }; int main(void) { Bint, double b; Aint* p b; p-foo(); // 多态调用输出 Bint,double::foo // p-bar(); // 正常调用但无虚函数机制 return 0; }数值型类型形参模板的类型形参不仅可以是类型名还可以是整数常量数值型参数用于控制模板行为。语法格式templatetypename T, int S 10 class Array { // ... };示例templatetypename T, int SIZE 10 class Array { private: T data[SIZE]; public: void print() const { for (int i 0; i SIZE; i) cout data[i] ; cout endl; } }; int main() { Arrayint, 5 arr1; // 指定大小为 5 Arraydouble arr2; // 使用默认大小 10 return 0; }支持缺省值如int SIZE 10。数值参数必须是编译时常量表达式。不支持浮点数、字符串等非整型常量。模板型类型形参模板的类型形参也可以是另一个类模板即模板模板参数用于接收模板作为参数。语法格式templatetemplatetypename class C Array class Sum { // ... };示例templatetypename T class Array { public: T data[10]; }; templatetypename T class List { public: T data[10]; }; templatetemplatetypename class Container Array class Sum { public: void add() { cout Using container type endl; } }; int main() { SumArray s1; // 显式指定容器类型 SumList s2; // 使用不同的模板 Sum s3; // 使用默认模板 Array return 0; }Container是一个模板模板参数表示接受一个单参数模板。必须使用templatetypename来限定其形式。若需更复杂的模板如双参数模板则需额外处理。模板二次编译机制编译器对模板进行两次编译这是理解模板错误的关键。第一次编译模板定义阶段发生在模板实例化之前。编译器仅检查模板本身的语法和基本语义。对于已知类型检查成员访问是否合法。对于未知类型如模板参数假设所有操作都合法。不会检查函数调用的实际有效性。第二次编译实例化阶段发生在模板被具体类型实例化之后。结合实际使用的类型实参重新检查所有代码。此时才能确定函数是否存在、成员是否可访问。错误示例解析#include iostream using namespace std; class A { public: void foo() {} }; templatetypename T void bar() { // gdjgjkgsfgkldd; // ❌ 第一次编译报错标识符未声明 A a; a.foo(); // ✅ 第一次编译通过a.foo() 合法 // a.fjoidjg(); // ❌ 第一次编译报错不存在该函数 T t; t.djfoijdf(); // ✅ 第一次编译通过假设存在 // t.djfoidfsdfsdjifod(); // ❌ 第一次编译报错非法使用 } int main() { barA(); // 触发实例化进入第二次编译 // 此时才检查 t.djfoijdf() 是否有效 —— 实际不存在报错 return 0; }关键结论第一次编译只做语法合法性检查不关心具体类型。第二次编译才真正验证调用的有效性。所有模板错误最终在实例化时暴露。避免在模板中使用未经验证的成员调用尤其是针对模板参数类型的成员。总结要点特性是否支持说明成员类模板模板型成员类型✅可在类模板内定义嵌套类模板成员虚函数✅支持多态可用于继承体系成员虚函数模板❌无法构建虚函数表因实例化延迟数值型模板参数✅整数常量可设缺省值模板模板参数✅接收类模板作为参数需templatetypename修饰二次编译机制⚠️先检查语法再结合实参验证有效性掌握这些规则有助于避免常见模板编程错误提升代码健壮性与可维护性。