STL 模板典型错误

本文详细探讨了STL模板使用中常见的五大问题,包括嵌套依赖的处理、依赖模板参数访问成员函数模板的限制、子模板与基模板的关系、零值初始化的注意事项以及类模板中成员虚函数的应用。通过实例解析,帮助开发者避免这些错误,提高代码质量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 嵌套依赖

在这里插入图片描述

#include <iostream>
using namespace std;
class A{
public:
    class B{
    public:
        void foo(){
            cout << "zhe e xin" << endl;
        }
    };
};
// 函数模板
template<class T> void Func(){
    // A::B b; // 嵌套依赖, 编译报错,在第一次编译的时候,会把B当做A的成员,那后面再跟个b就有语法错误了
    // 解决方法通过typename告诉编译器 B是A的嵌套类型
    typename A::B b;
    b.foo();
}

int main(void){
    Func<int>();
    return 0;
}

2. 依赖模板参数访问成员函数模板

在这里插入图片描述

  • 第一次编译时,发现用未知类型定义的对象访问成员函数模板,属于未知类型调用,而对于未知类型调用编译器是不允许<>存在的。
  • 而访问成员函数模板必须要加<>, 解决办法就是加template
#include <iostream>
using namespace std;
class A{
public:
    // 成员函数模板
    template<class T> void foo(){
        cout << "A::foo<T>" << endl;
    }
};

template<class D> void Func(){
    // 第一次编译时,D 还是未知类型
    D d;
    // d.foo<int>(); // 依赖模板参数访问成员函数模板, 编译报错
    // 发现用未知类型定义的对象访问成员函数模板,属于未知类型调用,而对于未知类型调用编译器是不允许<>存在的。
    // 需要再加一个template解决这个问题
    d.template foo<int>(); 
}

int main(void){
    Func<A>();	// A::foo<T>
    return 0;
}

3. 子模板访问基模板

在这里插入图片描述

  • 类模板不是类, 没有类的语义,所有父子类模板没有继承一说, 继承是子类与基类之间的事.
  • 所以图中第一句话就是一个伪命题,子类模板根本就不可能访问基类模板成员。
#include <iostream>
using namespace std;

template <class T>class Base{
public:
    int m_i;
    void foo(){
        cout << "Base<T>::foo()" << endl;
    }
};

// 子模板访问基类模板
// 这里的继承,是继承了一个未知类,未知类里有什么,编译器也不知道
template <class T, class D> class Derived: public Base<T>{
public:
    void bar(){
        // 所以编译器不会去基类里找m_i和foo这两个成员
        // 会先当前子类模板中看有没有这两个标识符的声明, 没有就去全局域,不会去基类模板Base中找
        // 解决方法是加一个作用域限定, 变成未知类型调用, 第一次编译就可以编译过去
        // 不要理解为去模板类Base中找 m_i foo, 因为压根不会去找。 
        // Base 在第一次编译的时候就是一个未知类
        Base<T>::m_i = 100;
        Base<T>::foo();
        // Base<T>:: 换成 this-> 也可以,属于未知类型调用,第一次编译可以编译过去
    }
};

int main(void){
    // 实例化,产生真正的父子类 Base, Derived
    Derived<int ,int> d;
    d.bar();
    return 0;
}

4. 零值初始化

在这里插入图片描述

#include <iostream>
using namespace std;

class Integer{
public:
    Integer():m_i(0){}
    friend ostream& operator<<(ostream& os, const Integer& c){
        os <<  c.m_i;
    }
private:
    int m_i;
};

template<class T>void Func(){
    // 在写模板的时候,定义了一个位置类型变量,且不知道怎么赋初始值
    // 就用类型形参加()
    T t = T(); // Integer() 相当于调用无参构造, int(), double().. 是取当前类型的零值
    cout << t << endl;
}

int main(void){
    Func<int>();
    Func<Integer>();
    return 0;
}
$ ./a.out 
0
0

5. 类模板中的成员虚函数

在这里插入图片描述

#include <iostream>
using namespace std;

template <class T>class Base{
public:
    int m_i;
    // 类模板中的普通成员函数可以是虚函数
    virtual void foo(){
        cout << "Base<T>::foo()" << endl;
    }
    // 成员函数模板不可以是虚函数, 下面编译报错
    /*
    virtual template<class M> void bar(){

    } 
    */
};

// 子模板访问基类模板
// 这里的继承,是继承了一个未知类,未知类里有什么,编译器也不知道
template <class T, class D> class Derived: public Base<T>{
public:
    void foo(){
        cout << "Derived<T, D>::foo()" << endl;
    }
};

int main(void){
    // 实例化,产生虚函数表
    // bar 是在被调用的时候才会被实例化出来,才有函数地址,逻辑上发生再产生函数表以后, 这是不行的
    Derived<int ,int> d;
    // 向上造型, 基类类型指针指向子类对象
    Base<int> * p_base = &d;
    // 表现出多态 
    p_base->foo();  // Derived<T, D>::foo()
    // p_base->bar<int>(); 逻辑上这时才有了bar函数实体,但人家早都把虚函数表构建出来
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值