当前位置: 首页 > article >正文

Effective C++ 规则43:学习处理模板化基类内的名称

1、背景

在 C++ 中,模板化基类为我们提供了强大的灵活性。然而,模板化基类的名称查找却经常会引发困惑,甚至导致编译错误。这是因为模板的名称查找规则与普通类不同。在普通类中,派生类可以直接访问基类的成员变量和成员函数,因为这些名称在编译时是确定的。然而,在模板化基类中,由于基类的定义依赖于模板参数,其成员的名称查找需要更多的信息来完成。如果派生类也是模板类,那么基类的成员名称只有在模板参数确定之后才能解析。

2、错误代码示例

以下代码可能会在 display() 调用处报错,提示找不到 display()。代码如下:

#include <iostream>

// 基类模板
template <typename T>
class Base {
public:
    void display() {
        std::cout << "Base display: " << value << std::endl;
    }
protected:
    T value = T{};
};

// 派生类模板
template <typename T>
class Derived : public Base<T> {
public:
    void show() {
        display(); // 编译器可能会报错
    }
};

int main() {
    Derived<int> d;
    d.show();
    return 0;
}

3、出现编译错误的原因

在 C++ 中,模板中的名称分为非依赖名称和依赖名称:

  • 非依赖名称(Non-dependent Name):与模板参数无关的名称。在模板实例化之前就可以解析。
  • 依赖名称(Dependent Name):与模板参数有关的名称,只有在模板实例化时才能解析。
    在上面的例子中,display() 是基类的成员函数,但因为基类是模板参数 T 的函数 Base,所以编译器无法确定 display() 是依赖名称还是非依赖名称。核心点有3项:
  • 派生类默认优先查找自己的成员。
  • 编译器在解析模板时,基类模板成员属于依赖名称,必须显式指明来源。
  • 使用 this-> 或 using 可以正确找到基类中的成员函数或变量。

4、解决方法

这里介绍两种常见的方式来解决模板化基类中名称查找的问题,还介绍一种在模板成员函数中访问基类模板镜头成员变量的 方法。

4.1、使用 this-> 明确访问基类成员

  • 访问基类成员函数,在派生类中通过 this-> 明确指定成员属于基类。修改后的代码:
template <typename T>
class Derived : public Base<T> {
public:
    void show() {
        this->display(); // 明确告知编译器,display 是从基类来的
    }
};

此时编译器知道 display 是从模板化基类中继承的成员,可以正确解析。

  • 访问基类的成员变量,类似地,基类的成员变量也会遇到相同的问题。例如:
template <typename T>
class Derived : public Base<T> {
public:
    void setValue(const T& val) {
        this->value = val; // 使用 this-> 访问基类成员变量
    }
};

4.2、使用 using 声明基类的成员

  • 访问基类成员函数
    可以通过 using 语句显式引入基类的成员,告诉编译器这些名称是从基类继承的。
template <typename T>
class Derived : public Base<T> {
public:
    using Base<T>::display; // 引入基类的 display 函数

    void show() {
        display();
    }
};
  • 访问基类成员变量
template <typename T>
class Derived : public Base<T> {
public:
    using Base<T>::value;

    void setValue(const T& val) {
        value = val;
    }
};

4.3、访问模板基类中静态成员

如果基类中有静态成员,同样需要显式指定:

template <typename T>
class Base {
public:
    static void staticFunc() {
        std::cout << "Base staticFunc" << std::endl;
    }
};

template <typename T>
class Derived : public Base<T> {
public:
    void callStatic() {
        Base<T>::staticFunc(); // 显式指定基类的静态成员
    }
};

5、总结

对于模板化基类的名称查找问题,this-> 和 using 是常用的解决方案:

  • 如果只需访问少量基类成员,this-> 更简洁。
  • 如果需要频繁访问基类成员,using 更高效且可读性更强。

http://www.kler.cn/a/515898.html

相关文章:

  • 打造本地音乐库
  • css动画水球图
  • 靶机复现-pikachu靶机文件包含漏洞
  • 重生之我在异世界学编程之C语言:深入指针篇(上)
  • mac 电脑上安装adb命令
  • C#性能优化技巧:利用Lazy<T>实现集合元素的延迟加载
  • linux+docker+nacos+mysql部署
  • 认识Django项目模版文件——Django学习日志(二)
  • Spring Boot整合Thymeleaf、JDBC Template与MyBatis配置详解
  • 【C语言】编译链接
  • 软考信安26~大数据安全需求分析与安全保护工程
  • 【C++笔记】哈希表底层实现的深度剖析
  • 车间设备数据采集解决方案
  • 智能体的核心技能之插件,插件详解和实例 ,扣子免费系列教程(11)
  • Elixir语言的Web开发
  • 知识产权API:助力金融业投资决策等场景提效!
  • 从理论到实践:Django 业务日志配置与优化指南
  • Facebook新品广告ROI一周速成攻略
  • 2.体验vue
  • 【若依】添加定时任务
  • ansible自动化运维实战--复制模块和用户模块(3)
  • 【0x06】HCI_Authentication_Complete事件详解
  • Solr与Elasticsearch 的对比与选型
  • Unity中关于实现 管道水流+瀑布流动+大肠蠕动效果笔记
  • HTML5 新表单属性详解
  • 深度剖析聚合 CPS 分销与 CPA 推广系统:打破收益枷锁,开启创业新篇