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

Effective C++ 条款45:运用成员函数模板接受所有兼容类型

Effective C++ 条款45:运用成员函数模板接受所有兼容类型


核心思想使用成员函数模板(member function templates)生成可接受兼容类型的函数,特别是泛型拷贝构造函数和赋值操作符,同时避免抑制编译器生成的默认特殊成员函数。

⚠️ 1. 智能指针的类型转换问题

问题根源

  • 希望智能指针能模拟内置指针的隐式转换(如派生类指针到基类指针)
  • 模板实例化后不同模板参数生成的是不同类,无法直接转换
  • 需要为每种可能的兼容类型单独编写构造函数,不现实

错误示例

template<typename T>
class SmartPtr {
public:explicit SmartPtr(T* realPtr); // 原始指针构造函数// 希望支持以下转换,但无法实现:// SmartPtr<Base> = SmartPtr<Derived>// SmartPtr<const T> = SmartPtr<T>
};

🚨 2. 成员函数模板解决方案

解决方案

  • 声明成员函数模板(泛型拷贝构造函数)
  • 使用类型约束确保安全转换

优化实现

template<typename T>
class SmartPtr {
public:template<typename U>SmartPtr(const SmartPtr<U>& other)  // 泛型拷贝构造: heldPtr(other.get()) { }     // 使用get()获取原始指针T* get() const { return heldPtr; } // 获取原始指针private:T* heldPtr; // 持有原始指针
};

类型安全约束

  • 添加编译期类型检查,确保U可隐式转换为T
template<typename T>
class SmartPtr {
public:template<typename U, typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>SmartPtr(const SmartPtr<U>& other): heldPtr(other.get()) { }// ...
};

⚖️ 3. 赋值操作与兼容性处理

问题场景

  • 需要支持不同类型的智能指针赋值
  • 同时要避免编译器自动生成默认拷贝操作

解决方案

  • 为赋值操作定义成员模板函数
  • 显式声明普通拷贝操作以避免被模板隐藏

完整实现

template<typename T>
class SmartPtr {
public:// 泛型拷贝构造template<typename U, typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>SmartPtr(const SmartPtr<U>& other);// 显式声明普通拷贝构造和赋值(防止被模板隐藏)SmartPtr(const SmartPtr&); SmartPtr& operator=(const SmartPtr&);// 泛型赋值操作符template<typename U, typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>SmartPtr& operator=(const SmartPtr<U>& other);// ... 其他成员 ...
};

注意事项

  • 成员函数模板不改变语言规则:拷贝构造函数不会阻止编译器生成默认拷贝构造函数
  • 若需要完全控制,可显式定义或使用=default/=delete

💡 关键设计原则

  1. 使用成员函数模板实现泛型构造
    成员函数模板可生成接受任意兼容类型的构造函数和赋值函数

    template<typename T>
    class SharedPtr {
    public:template<typename Y>explicit SharedPtr(Y* p);  // 从任意类型指针构造template<typename Y>SharedPtr(const SharedPtr<Y>& r); // 兼容类型拷贝构造
    };
    
  2. 添加类型转换约束
    使用std::enable_if和类型特征确保安全转换

    template<typename T>
    class SmartPtr {template<typename U, typename = std::enable_if_t<std::is_convertible_v<U*, T*>>>SmartPtr(const SmartPtr<U>&);
    };
    
  3. 显式声明默认函数
    避免成员模板隐藏编译器生成的默认函数

    template<typename T>
    class SmartPtr {
    public:// 显式声明拷贝操作SmartPtr(const SmartPtr&); SmartPtr& operator=(const SmartPtr&);// 成员模板构造函数...
    };
    

实战:跨类型智能指针赋值

class Base { /*...*/ };
class Derived : public Base { /*...*/ };SmartPtr<Base> pBase(new Base);
SmartPtr<Derived> pDerived(new Derived);// 使用成员模板实现兼容类型赋值
pBase = pDerived;  // 正确:通过泛型赋值操作符// 错误示例:类型不兼容
SmartPtr<int> pInt(new int);
// pBase = pInt; // 编译错误:类型约束阻止转换

类型特征约束进阶

// 使用更精确的约束:派生关系
template<typename T>
class SmartPtr {template<typename U, typename = std::enable_if_t<std::is_base_of_v<T, U> || std::is_convertible_v<U*, T*>>>SmartPtr(const SmartPtr<U>&);
};

总结成员函数模板允许类模板生成接受任意兼容类型的函数,是实现泛型拷贝构造和赋值操作的关键技术。通过添加编译期类型约束(如std::enable_if和类型特征)确保转换安全,同时显式声明默认拷贝操作以避免被模板隐藏。这一技术广泛用于智能指针、迭代器等需要类型灵活性的场景,是编写高级模板代码的必备技能。

http://www.dtcms.com/a/335304.html

相关文章:

  • Day2--滑动窗口与双指针--2090. 半径为 k 的子数组平均值,2379. 得到 K 个黑块的最少涂色次数,2841. 几乎唯一子数组的最大和
  • Linux软件编程:线程间通信
  • 【FreeRTOS】队列集
  • MySQL 插入数据提示字段超出范围?一招解决 DECIMAL 类型踩坑
  • 第三十七天(js前端数据加密和混淆)
  • Fixture Caliper 工具
  • GRPO(Group Relative Policy Optimization)公式速览
  • Scala面试题及详细答案100道(11-20)-- 函数式编程基础
  • 嵌入式软件架构设计之九: 双机通信之通信方式
  • 排列与组合
  • 超详细yolov8/11-obb旋转框全流程概述:配置环境、数据标注、训练、验证/预测、onnx部署(c++/python)详解
  • STM32标准库学习笔记
  • MM-Spatial和Spatial-MLLM论文解读
  • 【力扣-多数元素 JAVA/Python】
  • CD4+ T细胞激活区分抗PD-L1联合抗CTLA4疗法与单药抗PD-L1治疗的响应差异-空间最近邻分析
  • 民法学学习笔记(个人向) Part.5
  • 【最后203篇系列】032 OpenAI格式调用多模型实验
  • 39.离散化与哈希
  • 数据结构:二叉树的遍历 (Binary Tree Traversals)
  • 杂记 03
  • v-scale-scree: 根据屏幕尺寸缩放内容
  • 基于Python的电影评论数据分析系统 Python+Django+Vue.js
  • 防御保护12-14
  • tmux常用命令
  • Flamingo
  • KingbaseES主备读写分离集群安装教程
  • 字节数据流
  • 北汽新能源半年报:双品牌战略拉动销量增长,多元布局促进转化
  • PIDGen!DecodeProdKey函数分析之四个断点
  • 【大模型应用开发 3.RAG技术应用与Faiss向量数据库】