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

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.dtcms.com/a/333778.html

相关文章:

  • 大模型幻觉涉及的违约责任探讨
  • Chrome插件开发全指南
  • K 近邻算法(KNN)及其应用解析
  • strings命令和findstr命令验证iso文件中ntkrnlmp.exe系统版本
  • 昇腾AI自学Day1-- 深度学习基础工具与数学
  • Lecture 9: Concurrency 2
  • AAAI爆款:目标检测新范式,模块化设计封神之作
  • fs模块_写入文件
  • 高可用双向存储服务GlusterFS
  • 【SpringBoot】SpringBoot 整合JDBC、Mybatis、Druid
  • PCA降维理论详解
  • Spring Boot 拦截器详解
  • 固定资产管理系统 OCR 识别功能技术解析
  • 无脑整合springboot2.7+nacos2.2.3+dubbo3.2.9实现远程调用及配置中心
  • 强制从不抱怨环境。
  • [Julia] 网络和流
  • vue2 + SimpleMindMap 制作思维导图
  • 野指针:程序崩溃的隐形杀手
  • 订单状态定时处理(Spring Task 定时任务)
  • 机械学习---词向量转化评价,附代码实例
  • 力扣(接雨水)——单调栈
  • 第454题.四数相加II
  • JavaWeb开发_Day12
  • 基于Selenium的web自动化框架
  • 电视同轴电缆全面指南:从基础到应用,批量测量一键计量
  • 第四章:大模型(LLM)】06.langchain原理-(2)langchain Chain的使用方法
  • 力扣top100(day04-03)--二分查找
  • MqSQL中的《快照读》和《当前读》
  • [论文笔记] WiscKey: Separating Keys from Values in SSD-Conscious Storage
  • Linux core dump