Effective C++ 条款42:了解 typename 的双重含义

Effective C++ 条款42:了解typename的双重含义

核心思想在模板声明中,typenameclass可互换使用,但在模板内部,typename必须用于显式指明嵌套从属类型名称(nested dependent type name),以避免编译器解析歧义。对于非从属名称或基类成员列表中的嵌套从属类型名称,不得使用typename

⚠️ 1. typename的两种用途

用法对照

场景关键字示例说明
模板参数声明class/typenametemplate<class T>template<typename T>两者完全等价
嵌套从属类型名称前缀typenametypename T::const_iterator it;必须使用typename标识类型
基类列表中的名称class Derived: public Base<T>::Nested { ... }基类列表中不能使用typename
初始化列表中的名称Derived(int x) : Base<T>::Nested(x) { ... }成员初始化列表不能使用typename

代码示例

template<typename T>
class MyVector {
public:// 嵌套从属类型名称:必须使用typenametypedef typename T::iterator iterator; // 正确:typename声明iterator是类型// 错误:缺少typename导致编译错误// typedef T::const_iterator const_iterator;void print(const T& container) {// 嵌套从属类型名称:必须使用typenametypename T::const_iterator cit = container.begin(); // 正确// 非从属名称:不需要typenameint value = 42; // 非从属名称,直接使用}
};

🚨 2. typename的规则与例外

决策矩阵

场景是否使用typename原因示例
模板参数声明可选(class/typename)两者等价template<typename T>
嵌套从属类型名称前必须避免解析歧义typename T::iterator it;
基类列表中的嵌套类型禁止语法规定class Derived : Base<T>::Nested { ... }
成员初始化列表中的嵌套类型禁止语法规定Derived() : Base<T>::Nested() { ... }
非从属名称禁止不需要int value;
显式特化/实例化禁止不在模板定义中在特化中直接使用具体类型

错误使用案例

template<typename T>
class Widget {
public:// 错误:在基类列表中使用typename// class WidgetDerived : typename Base<T>::Nested { ... };// 错误:在初始化列表中使用typename// Widget() : typename Base<T>::Nested() { ... }// 错误:非从属名称使用typename// typename int value;
};

嵌套从属名称解析规则

template<typename T>
void process(const T& container) {// 假设T是一个容器类型,有const_iterator成员类型T::const_iterator it1 = container.begin(); // 可能被解析为静态成员变量(错误)typename T::const_iterator it2 = container.begin(); // 正确:明确为类型
}

⚖️ 3. 最佳实践与适用场景

场景1:标准容器迭代器

template<typename Container>
void printContainer(const Container& c) {// 必须使用typename标识嵌套从属类型typename Container::const_iterator it;for (it = c.begin(); it != c.end(); ++it) {std::cout << *it << ' ';}
}

场景2:模板元编程中的类型萃取

template<typename T>
struct TypeTraits {// 使用typename提取迭代器关联的类型typedef typename T::value_type value_type;typedef typename T::iterator_category iterator_category;
};// 使用
template<typename Iter>
void advance(Iter& it, int n) {// 使用typename获取类型特征typename TypeTraits<Iter>::iterator_category category;// ... 根据分类实现advance
}

现代C++增强

// C++11 using别名模板
template<typename T>
using RemoveReference = typename std::remove_reference<T>::type;// C++14起,标准库类型萃取有_v和_t版本,避免typename
template<typename T>
void func() {std::remove_reference_t<T> x; // 等价于typename std::remove_reference<T>::type
}

💡 关键设计原则

  1. 模板参数声明自由选择

    // class和typename在模板参数声明中完全等价
    template<class T> class A;
    template<typename T> class B;
    
  2. 嵌套从属类型必须加typename

    template<typename T>
    class Demo {
    public:// T::SubType 可能是类型或静态成员typename T::SubType member; // 必须加typename
    };
    
  3. 基类和初始化列表禁止加typename

    template<typename T>
    class Derived : public Base<T>::Nested { // 基类列表中不能加typename
    public:Derived(int x) : Base<T>::Nested(x) { ... } // 初始化列表中不能加
    };
    

依赖类型解析实战

template<typename Iter>
auto getValue(Iter it) -> typename std::iterator_traits<Iter>::value_type {return *it;
}// C++14起可用decltype(auto)简化
template<typename Iter>
decltype(auto) getValueSimplified(Iter it) {return *it;
}

模板元编程中的typename

// 检查T是否有名为type的嵌套类型
template<typename T, typename = void>
struct HasType : std::false_type {};template<typename T>
struct HasType<T, typename std::void_t<typename T::type>> : std::true_type {};// 使用
static_assert(HasType<std::underlying_type<int>>::value, "has type");

te

struct HasType<T, typename std::void_t> : std::true_type {};

// 使用
static_assert(HasType<std::underlying_type>::value, “has type”);

总结<:typename在C++模板编程中有双重角色。在声明模板参数时,它与class等价;在模板内部,它必须用于标识嵌套从属类型名称,以避免编译器将类型解释为静态成员。在基类列表和成员初始化列表中,即使出现嵌套从属类型名称,也不得使用typename。随着C++14引入_t_v类型萃取辅助,部分场景可避免显式使用typename,但在通用模板编程中仍需谨慎遵循规则。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95665.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95665.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ENCOPIM, S.L. 参展 AUTO TECH China 2025 广州国际汽车技术展览会

ENCOPIM, S.L. 参展 AUTO TECH China 2025 广州国际汽车技术展览会2025年11月21-24日中国进出口商品交易会展馆D区(广州)AUTO TECH China 2025同期&#xff1a;第二十三届广州车展即将盛大开幕展商推荐ENCOPIM, S.L.展位号&#xff1a;3916企业简介&#xff1a;ENCOPIM, S.L.于…

30 HTB Soccer 机器 - 容易

主要知识点 第一阶段&#xff1a;侦查 nmap nmap快速扫描&#xff1a; oxdfhacky$ nmap -p- --min-rate 10000 10.10.11.194 Starting Nmap 7.80 ( https://nmap.org ) at 2023-06-04 13:32 EDT Nmap scan report for 10.10.11.194 Host is up (0.093s latency). Not shown:…

阿里云机器翻译接口SDK-RAM权限配置

用户授权翻译权限在数字化时代&#xff0c;短信作为企业与用户沟通的重要桥梁&#xff0c;其高效、可靠的送达直接影响业务转化与用户体验。SDK&#xff08;软件开发工具包&#xff09;的出现极大简化了短信功能的集成过程&#xff0c;让开发者能够快速在应用中嵌入短信验证、通…

ESXI 6.7服务器时间错乱问题

1. 设置ESXI服务器&#xff1a;在此主机上手动配置日期和时间管理-服务-ntpd-鼠标右键-策略-手动启动和停止&#xff0c;状态已停止管理-系统-时间和日期-编辑设置-检查是否选择了【在此主机上手动配置日期和时间】ntp服务状态已停止ntp服务器已停止2. 停止所有虚拟机自动更新时…

CV 医学影像分类、分割、目标检测,之【皮肤病分类】项目拆解

CV 医学影像分类、分割、目标检测&#xff0c;之【皮肤病分类】项目拆解第1-12行&#xff1a;导入库第14-17行&#xff1a;读取标签文件第19-21行&#xff1a;获取疾病名称第23-26行&#xff1a;获取图片名列表第28-35行&#xff1a;筛选有标签的图片第38-43行&#xff1a;提取…

【JavaEE】多线程 -- 线程状态

目录六大状态举例说明六大状态 New 新建状态&#xff1a;线程还没出创建&#xff0c;只有Thread 实例化的对象&#xff0c;调用start 方法之前的状态。Runnable 运行状态&#xff1a;被系统调度后&#xff0c;CPU 正在执行的&#xff0c;Ready 就绪态&#xff0c;系统调度&…

网络流初步

网络流初步 文章目录网络流初步概念介绍最大流费用流概念介绍 网络流不同之处在于它的本质图论&#xff0c;但是把图论的某些概念换了一个说法而已&#xff0c;初步只要了解网络流的各个概念就可以明白的很快。 下述概念是本人自己定义的&#xff0c;对于网络流的题目做的还不…

[系统架构设计师]系统架构基础知识(一)

[系统架构设计师]系统架构基础知识&#xff08;一&#xff09; 一.计算机系统基础知识 1.计算机系统概述 硬件软件及网络组成的系统 2.计算机硬件基础知识 冯 诺依曼结构&#xff1a;运算器&#xff0c;控制器&#xff0c;存储器&#xff0c;输入设备&#xff0c;输出设备 专用…

深入解析Java代理模式:灵活控制对象访问的核心技术

在日常开发中&#xff0c;我们常遇到这样的场景&#xff1a;需要控制对象访问权限、优化高成本操作&#xff0c;或给方法添加额外功能&#xff08;如日志、事务&#xff09;。代理模式&#xff08;Proxy Pattern&#xff09; 正是解决这类问题的金钥匙。作为结构型设计模式的代…

【学习笔记】Java并发编程的艺术——第9章 Java中的线程池

第9章 Java中的线程池 线程池优势&#xff1a; ①减少资源消耗 ②提高响应速度 ③统一管理 9.1 线程池的实现原理 当任务来后 ①判断核心线程池是否已满&#xff0c;若未满&#xff0c;创建一个核心线程来执行任务 ②若无空闲核心线程且核心线程已满&#xff0c;则将任务放入任…

Mybatis学习笔记(九)

常见问题与解决方案 简要描述&#xff1a;总结MyBatis-Plus开发过程中常见的问题、错误及其解决方案&#xff0c;帮助开发者快速定位和解决问题。 核心概念&#xff1a; 常见错误&#xff1a;开发中经常遇到的错误类型性能问题&#xff1a;性能相关问题的排查和解决配置问题&am…

数据类型 list

一、介绍类似于数组&#xff0c;顺序表&#xff0c;deque结构图特点&#xff1a;元素有序&#xff0c;元素允许重复由于头尾高效插入删除&#xff0c;可以模拟栈&#xff0c;队列二、常见 list 命令1、lpush key elem [elem ...]头插元素&#xff0c;返回值列表长度2、lrange k…

pyqt5无法显示opencv绘制文本和掩码信息

背景&#xff1a;pyqt5无法显示opencv绘制的标签和mask&#xff1b;我们在使用YOLO做实例分割做推理时&#xff0c;会使用opencv做后处理结果绘制&#xff08;含标签绘制和掩码绘制&#xff09;&#xff1b;结果opencv绘制的解码却无法在pyqt的解码上面显示。pyqt转换代码如下&…

如何生成严格递增的分布式id?

本文字数&#xff1a;2604字预计阅读时间&#xff1a;15分钟01引言在现有分布式系统中&#xff0c;面对增长迅速的业务数据&#xff0c;id生成一直是非常重要的一环。而分布式系统的id生成方案需要满足几个重要特性&#xff1a;容错高可用、高性能高并发、全局唯一。02技术背景…

【LeetCode】二叉树相关算法题

目录1、二叉树介绍【1】核心概念【2】关键特性2、算法题【1】二叉树的前序遍历【2】二叉树的后序遍历1、二叉树介绍 【1】核心概念 结构含义节点结构二叉树由节点组成&#xff0c; 每个节点包含一个数据元素和最多两个子节点&#xff1a;左子节点和右子节点根节点树的顶部节点…

Vulnhub Deathnote靶机复现攻略

一、靶机安装 下载地址&#xff1a;https://download.vulnhub.com/deathnote/Deathnote.ova 下载好后使用VB打开&#xff0c;配置如下 二、主机发现 使用相同连接方式的kali进行后续操作(172.16.2.7)根据mac地址进行确认。 nmap -sn 172.16.2.1/24 三、端口扫描 端口开放了…

DevEco Studio 6.0.0 元服务页面跳转失败

背景&#xff0c;我使用最新的编辑器DevEco Studio 6.0.0&#xff0c;编写一个元服务&#xff0c;发现使用跳转页面的时候失败了&#xff01;然后查看官方文档&#xff0c;两种方式都测试了&#xff0c;发现都不行。 方法1&#xff1a;Navigation路由跳转无效&#xff0c;见官方…

docker重启或系统重启后harbor自动启动

docker重启或系统重启后harbor自动启动docker重启或系统重启后harbor自动启动方法 1&#xff1a;在 docker-compose.yml 中配置重启策略&#xff08;推荐&#xff09;方法 2&#xff1a;创建 Systemd 服务&#xff08;更可靠&#xff09;方法 3&#xff1a;使用 Docker 的 Rest…

OpenZeppelin Contracts 架构分层分析

OpenZeppelin Contracts 是一个面向以太坊&#xff08;及兼容 EVM 的区块链&#xff09;生态系统的​​模块化、安全性优先、标准兼容的智能合约库​​。其内部代码按照功能职责与抽象层级&#xff0c;可系统性地划分为多个逻辑层次。理解这些层次及其依赖关系&#xff0c;对于…

Java-JVM的内存模型

一.JVM内存模型JVM内存模型可以从进程生命周期和线程生命周期1.线程生命周期每个线程都会有自己各自一份数据&#xff0c;不会存在线程安全问题1.程序计数器指示当前线程执行的字节码指令的行号&#xff0c;以便线程执行时可以回到正确的位置2.虚拟机栈线程私有的&#xff0c;与…