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

1、问题背景

在设计类的成员函数时,我们通常会希望能够处理多种不同但兼容的类型。例如:

  • 传入的类型与类的模板类型不同,类模板的类型可能是 T,但我们希望成员函数能够接受与 T 兼容的其他类型,如 const T&、T&& 或者类型 U,其中 U 可以隐式转换为 T。
  • 避免不必要的拷贝,如果类的成员函数只接受固定的类型(如 T 或 const T&),对于可隐式转换的类型,编译器可能会生成不必要的拷贝或临时对象。

2、解决方法可以使用成员函数模板

通过使用 成员函数模板,我们可以编写接受兼容类型的函数声明。成员函数模板是定义在类模板内部的模板化成员函数,它的类型参数独立于类模板的类型参数。
示例 1:简单的成员函数模板:

#include <iostream>
#include <string>template <typename T>
class Container {
public:// 接收与 T 兼容的任意类型template <typename U>void setValue(U&& value) {data = std::forward<U>(value);}void printValue() const {std::cout << "Value: " << data << std::endl;}private:T data;
};int main() {Container<std::string> container;container.setValue("Hello, World!"); // 传入 const char* 类型container.printValue();container.setValue(std::string("New Value")); // 传入 std::string 类型container.printValue();return 0;
}
  • setValue 是一个成员函数模板,模板参数 U 独立于类模板参数 T。
  • setValue 使用了 完美转发 (std::forward) 来避免不必要的拷贝。
  • 该函数可以接受任何能够隐式转换为 T 的类型。
    示例 2:假设我们需要实现一个简单的数学容器类,可以接受不同类型的值进行加法操作:
#include <iostream>template <typename T>
class MathContainer {
public:MathContainer(T value) : data(value) {}// 接收任意与 T 兼容的类型进行加法template <typename U>void add(U value) {data += value; // 假设 T 和 U 都支持 operator+=}T getValue() const {return data;}private:T data;
};int main() {MathContainer<int> intContainer(10);intContainer.add(5);      // 传入 int 类型intContainer.add(2.5);    // 传入 double 类型std::cout << "Result: " << intContainer.getValue() << std::endl; // 输出17MathContainer<double> doubleContainer(3.5);doubleContainer.add(4);   // 传入 int 类型doubleContainer.add(1.5); // 传入 double 类型std::cout << "Result: " << doubleContainer.getValue() << std::endl; // 输出9return 0;
}

3、成员函数模板的优势

  • 支持多种类型,成员函数模板可以使类的成员函数接受比类模板参数更广泛的类型; 减少了对类型的限制,提高了代码的灵活性。
  • 避免不必要的类型转换,使用模板参数时,编译器会自动处理类型推导,避免显式的类型转换;
  • 提升代码复用性,成员函数模板使得类可以处理更多的情况,而不需要重新定义多个特化版本。
  • 提高性能,通过完美转发(std::forward)等技术,可以减少不必要的对象拷贝或移动。

4、潜在问题与注意事项

  • 模板函数匹配复杂性,当类模板和成员函数模板的参数类型匹配过于灵活时,可能会导致函数重载解析变得复杂,甚至引入意外的二义性。
  • 依赖类型的操作符,成员函数模板中通常会使用模板参数执行某些操作(如 +、= 等),这需要确保模板参数支持这些操作符,否则会导致编译错误。
  • SFINAE 和 Concepts,如果希望限制模板参数类型,可以使用 SFINAE(如 std::enable_if)或 C++20 的 Concepts 来约束参数类型。例如:
template <typename U>
auto setValue(U&& value) -> std::enable_if_t<std::is_convertible_v<U, T>> {data = std::forward<U>(value);
}

std::enable_if_t是C++17引入的,它的目的是对模板参数进行限制,使得模板函数只适用于某些符合特定条件的类型。

5、总结

使用成员函数模板可以显著提升类的灵活性和通用性,尤其是在处理与模板参数兼容的其他类型时。这种方法既能提高代码复用性,也能避免不必要的性能开销。规则的核心是:在设计类时,不要把类模板的参数限制得太死,通过成员函数模板,可以让类更适应不同的应用场景,同时保持高效和简洁的代码风格。

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

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

相关文章

Tensor 基本操作4 理解 indexing,加减乘除和 broadcasting 运算 | PyTorch 深度学习实战

前一篇文章&#xff0c;Tensor 基本操作3 理解 shape, stride, storage, view&#xff0c;is_contiguous 和 reshape 操作 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started Tensor 基本使用 索引 indexing示例代码 加减…

STM32 硬件I2C读写

单片机学习&#xff01; 目录 前言 一、步骤 二、配置I2C外设 2.1 开启I2C外设和GPIO口时钟 2.2 GPIO口初始化为复用开漏模式 2.3 结构体配置I2C 2.4 使能I2C 2.5 配置I2C外设总代码 三、指定地址写时序 3.1 生产起始条件S 3.2 监测EV5事件 3.3 发送从机地址 3.4 …

使用 Elasticsearch 导航检索增强生成图表

作者&#xff1a;来自 Elastic Louis Jourdain 及 Ivan Monnier 了解如何使用知识图谱来增强 RAG 结果&#xff0c;同时在 Elasticsearch 中高效存储图谱。本指南探讨了根据用户查询动态生成知识子图的详细策略。 检索增强生成 (RAG) 通过将大型语言模型 (LLM) 的输出基于事实数…

【后端开发】字节跳动青训营之性能分析工具pprof

性能分析工具pprof 一、测试程序介绍二、pprof工具安装与使用2.1 pprof工具安装2.2 pprof工具使用 资料链接&#xff1a; 项目代码链接实验指南pprof使用指南 一、测试程序介绍 package mainimport ("log""net/http"_ "net/http/pprof" // 自…

【落羽的落羽 数据结构篇】算法复杂度

文章目录 一、数据结构和算法简介二、算法复杂度1. 时间复杂度2. 空间复杂度 一、数据结构和算法简介 数据结构是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的数据元素的集合。没有一种单一的数据结构对所有用途都有用&#xff0c;所以我们要学…

VsCode安装文档

一、下载 进入VS Code官网&#xff1a;Visual Studio Code - Code Editing. Redefined&#xff0c;点击 DownLoad for Windows下载windows版本 当然也可以点击旁边的箭头&#xff0c;下载Windows版本 或 Mac OS 版本 备注&#xff1a; Stable&#xff1a;稳定版Insiders&#…

32、【OS】【Nuttx】OSTest分析(1):stdio测试(二)

背景 接上篇wiki 31、【OS】【Nuttx】OSTest分析&#xff08;1&#xff09;&#xff1a;stdio测试&#xff08;一&#xff09; 继续stdio测试的分析&#xff0c;上篇讲到标准IO端口初始化&#xff0c;单从测试内容来说其实很简单&#xff0c;没啥可分析的&#xff0c;但这几篇…

机器学习-核函数(Kernel Function)

核函数&#xff08;Kernel Function&#xff09;是一种数学函数&#xff0c;主要用于将数据映射到一个更高维的特征空间&#xff0c;以便于在这个新特征空间中更容易找到数据的结构或模式。核函数的主要作用是在不需要显式计算高维特征空间的情况下&#xff0c;通过内积操作来实…

计算机网络 (60)蜂窝移动通信网

一、定义与原理 蜂窝移动通信网是指将一个服务区分为若干蜂窝状相邻小区并采用频率空间复用技术的移动通信网。其原理在于&#xff0c;将移动通信服务区划分成许多以正六边形为基本几何图形的覆盖区域&#xff0c;称为蜂窝小区。每个小区设置一个基站&#xff0c;负责本小区内移…

win32汇编环境,函数的编写与调用、传值或返回值等

;运行效果 ;win32汇编环境,函数的编写与调用、传值或返回值等 ;函数在被调用的时候&#xff0c;如果此函数实体在前面&#xff0c;可以不用声明。如果实体在后面&#xff0c;则需要先声明。类似于下面的DlgProc函数&#xff0c;因为它的实体在后面&#xff0c;所以需要在调用之…

Oracle 创建用户和表空间

Oracle 创建用户和表空间 使用sys 账户登录 建立临时表空间 --建立临时表空间 CREATE TEMPORARY TABLESPACE TEMP_POS --创建名为TEMP_POS的临时表空间 TEMPFILE /oracle/oradata/POS/TEMP_POS.DBF -- 临时文件 SIZE 50M -- 其初始大小为50M AUTOEXTEND ON -- 支持…

Java 大视界 -- Java 大数据中的异常检测技术与应用(61)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

Anonymous,Github 匿名化工具

一.Github 匿名化工具 Anonymous&#xff0c;会为 github 自动生成一个匿名化的URL&#xff0c;保护隐私和双盲评审 待添加...

Linux(Centos、Ubuntu) 系统安装jenkins服务

该文章手把手演示在Linux系统下如何安装jenkins服务、并自定义jenkins数据文件位置、以及jenkins如何设置国内镜像源加速&#xff0c;解决插件下载失败问题 安装方式&#xff1a;war包安装 阿里云提供的war下载源地址&#xff1a;https://mirrors.aliyun.com/jenkins/war/?s…

MongoDB实训:电子商务日志存储任务

一、实验目的 1. 理解如何通过Java API连接MongoDB数据库。 2. 学习在Java中使用MongoDB进行数据库操作&#xff0c;包括插入数据、查询数据以及数据统计等。 3. 掌握电子商务日志数据在MongoDB中的存储和操作方法。 二、实验环境准备 1. JAVA环境准备&#xff1a;确保…

计算机网络 (59)无线个人区域网WPAN

前言 无线个人区域网&#xff08;WPAN&#xff0c;Wireless Personal Area Network&#xff09;是一种以个人为中心&#xff0c;采用无线连接方式的个人局域网。 一、定义与特点 定义&#xff1a;WPAN是以个人为中心&#xff0c;实现活动半径小、业务类型丰富、面向特定群体的无…

从spec到iso的koji使用

了解一下Linux发行版流程&#xff1a;:从spec到iso的koji使用 for Fedora 41。 Fedora 41有24235个包&#xff0c;我们选择 minimal 的几十个源码包&#xff0c;百多个rpm包构建。 配3台服务器 40C64G 44C64G 80C128G&#xff0c;有点大材小用&#xff0c;一台就够了 &#xf…

20250124-注意力机制(5-7)【3/3完结】 ——已复现

Attention Is All You Need&#xff08;注意力就是你所需要的一切&#xff09;&#xff08;5-7&#xff09;【3/3完结】 ——已复现 20250124-注意力机制&#xff08;1-2&#xff09;【1/3】 ——已复现-CSDN博客 20250124-注意力机制&#xff08;3-4&#xff09;【2/3】 ——已…

22_解析XML配置文件_List列表

解析XML文件 需要先 1.【加载XML文件】 而 【加载XML】文件有两种方式 【第一种 —— 使用Unity资源系统加载文件】 TextAsset xml Resources.Load<TextAsset>(filePath); XmlDocument doc new XmlDocument(); doc.LoadXml(xml.text); 【第二种 —— 在C#文件IO…

[JavaScript] ES6及以后版本的新特性

文章目录 箭头函数&#xff08;Arrow Functions&#xff09;为什么需要箭头函数&#xff1f;箭头函数的完整语法箭头函数中的 this实用场景 解构赋值&#xff08;Destructuring Assignment&#xff09;为什么需要解构赋值&#xff1f;数组解构赋值的完整用法对象解构赋值的完整…