Effective Modern C++ 条款26:避免在通用引用上重载

在C++编程中,函数重载是一项强大的特性,它允许我们为不同的参数类型提供不同的实现。然而,当涉及到通用引用(universal references)时,重载可能会带来意想不到的问题。Effective Modern C++的条款26明确指出:避免在通用引用上进行重载。本文将通过一个具体的例子,深入探讨这一条款的重要性,并分析其背后的原因。


示例:logAndAdd函数的重载问题

假设我们需要编写一个函数logAndAdd,它的功能是将一个名字记录到日志中,并将其添加到一个全局的std::multiset<std::string>集合中。为了提高效率,我们考虑使用通用引用和完美转发技术。

初始实现

std::multiset<std::string> names;void logAndAdd(const std::string& name) {auto now = std::chrono::system_clock::now();log(now, "logAndAdd");names.emplace(name);
}

这个实现没有问题,但效率不高。对于右值参数(如临时对象或字符串字面量),它仍然会进行一次拷贝操作。

使用通用引用优化

为了提高效率,我们重写logAndAdd,使用通用引用和完美转发:

template<typename T>
void logAndAdd(T&& name) {auto now = std::chrono::system_clock::now();log(now, "logAndAdd");names.emplace(std::forward<T>(name));
}

这样,右值参数会被移动而不是拷贝,字符串字面量也会直接构造,避免了不必要的临时对象。

支持索引参数的重载

有些情况下,用户可能需要通过索引查找名字。为了支持这种需求,我们为logAndAdd添加了一个重载版本:

void logAndAdd(int idx) {auto now = std::chrono::system_clock::now();log(now, "logAndAdd");names.emplace(nameFromIdx(idx));
}

问题的出现

现在,我们发现当传递一个short类型的索引时,程序会出错:

short nameIdx = 42;
logAndAdd(nameIdx); // 错误!

为什么会出现这个问题呢?让我们仔细分析。


问题分析:重载解析规则

C++的重载解析规则决定了在多个重载函数中选择哪一个函数。规则是:精确匹配优先于类型提升的匹配

在我们的例子中,logAndAdd有两个重载版本:

  1. template<typename T> void logAndAdd(T&& name)
  2. void logAndAdd(int idx)

当传递一个short类型的参数时,会发生以下情况:

  • 通用引用版本:模板参数T会被推导为short,因此函数签名变为void logAndAdd(short&& name)。这是一个精确匹配,因为short类型的参数可以与short&&完美匹配。
  • int版本short类型可以通过类型提升转换为int,因此这个版本也是一个候选函数。

根据重载解析规则,通用引用版本会优先被调用。然而,logAndAdd(short&& name)的实现会尝试将short类型的参数转发给std::multiset<std::string>emplace函数,而std::string没有接受short类型的构造函数,因此编译会失败。


为什么通用引用重载会导致问题?

通用引用(T&&)在C++中是非常“贪婪”的,它几乎可以匹配任何类型的参数。具体来说:

  • 对于左值,T&&会被推导为T&,因此函数会接受左值参数。
  • 对于右值,T&&会保持为右值引用。

这意味着,通用引用版本的函数几乎可以匹配所有类型的参数,而不仅仅是预期的那些。当与非通用引用的重载函数(如int版本)同时存在时,通用引用版本会“吞噬”比预期更多的参数类型,导致意外的行为。


解决方案:避免在通用引用上重载

为了避免上述问题,Effective Modern C++建议避免在通用引用上进行重载。如果必须支持不同的参数类型,可以考虑以下替代方法:

1. 避免重载,使用模板特化

如果需要为特定类型提供不同的实现,可以使用模板特化:

template<typename T>
void logAndAdd(T&& name) {// 通用实现names.emplace(std::forward<T>(name));
}template<>
void logAndAdd<int>(int idx) {// 专门为int类型实现names.emplace(nameFromIdx(idx));
}

这样,int类型的参数会调用特化版本,而其他类型会调用通用版本。

2. 使用SFINAE技术

SFINAE(Substitution Failure Is Not An Error)技术可以有条件地启用函数重载。例如,可以编写一个函数,仅在参数类型为int时有效:

template<typename T>
void logAndAdd(T&& name, std::enable_if_t<!std::is_same_v<T, int>, bool> = true) {names.emplace(std::forward<T>(name));
}void logAndAdd(int idx) {names.emplace(nameFromIdx(idx));
}

这样,当传递int类型的参数时,会优先调用非模板版本;而对于其他类型,会调用模板版本。


总结

在C++编程中,函数重载是一项强大的特性,但与通用引用结合使用时,可能会带来意想不到的问题。通用引用的“贪婪”匹配特性会导致重载解析优先选择通用引用版本,而忽略其他可能更合适的重载函数。

为了避免这类问题,Effective Modern C++建议避免在通用引用上进行重载。如果需要支持不同的参数类型,可以考虑使用模板特化或SFINAE技术来实现更精细的控制。

记住,通用引用的强大之处在于其灵活性,但过度使用或不当使用可能会导致代码难以维护和调试。在实际开发中,审查代码并确保没有不必要的通用引用重载,是编写高效、可靠C++代码的关键。

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

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

相关文章

OpenLayers数据源集成 -- 章节一:图像图层详解

前言在前面的文章中&#xff0c;我们学习了OpenLayers的基础控件操作。本文将深入探讨OpenLayers中的图像图层&#xff08;ImageLayer&#xff09;功能&#xff0c;通过一个完整的示例来展示如何使用ImageArcGISRest数据源加载ArcGIS服务&#xff0c;并详细解释图层配置、事件监…

通义万相wan2.2 Fun系列--Camera镜头控制与lnp首尾帧视频模型

上节内容讲解了wan2.2 fun control本节内容对wan2.2 fun系列模型的camera镜头控制模型与lnp首尾帧视频模型进行测试与讲解。 Wan2.2-Fun-Camera-Control是阿里基于Wan2.2框架推出的图生视频运镜控制模型 。它支持512、768、1024等多分辨率的视频预测&#xff0c;以81帧、每秒16…

JavaSE 集合从入门到面试:全面解析与实战指南

JavaSE 集合从入门到面试&#xff1a;全面解析与实战指南 在 Java 编程中&#xff0c;集合是处理数据的核心工具&#xff0c;几乎所有 Java 应用都会用到集合框架。从简单的列表存储到复杂的数据分析&#xff0c;集合框架提供了丰富的数据结构和操作方法。本文将从基础概念到面…

自建云音乐服务器:Navidrome+cpolar让无损音乐随身听

文章目录前言1. 安装Docker2. 创建并启动Navidrome容器3. 公网远程访问本地Navidrome3.1 内网穿透工具安装3.2 创建远程连接公网地址3.3 使用固定公网地址远程访问前言 “想听自己的无损音乐还要开会员&#xff1f;”——音乐发烧友小王的烦恼。商业音乐平台音质压缩&#xff…

C3P0连接池适配HGDB

文章目录文档用途详细信息文档用途 讲解常用的并且需要与数据库进行交互的开源框架C3P0&#xff0c;以及C3P0框架是如何适配HGDB的。 详细信息 1.C3P0概述 C3P0是一个开源的JDBC连接池&#xff0c;它实现了数据源和JNDI绑定&#xff0c;支持JDBC3规范和JDBC2的标准扩展。目…

ZeroGPU Spaces 加速实践:PyTorch 提前编译全解析

ZeroGPU 让任何人都能在 Hugging Face Spaces 中使用强大的 Nvidia H200 硬件&#xff0c;而不需要因为空闲流量而长期占用 GPU。 它高效、灵活&#xff0c;非常适合演示&#xff0c;不过需要注意的是&#xff0c;ZeroGPU 并不能在所有场景下完全发挥 GPU 与 CUDA 栈的全部潜能…

8.ImGui-输入框

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 上一个内容&#xff1a;7.ImGui-单选框和复选框 单行输入框使用 ImGui::InputText()&#xff0c;下图中…

2025年- H120-Lc28. 找出字符串中第一个匹配项的下标(数组)--Java版

1.题目2.思路 短的子串相对不变 所以我们用长的字符串去截取in个长度的子串做遍历 如果两者相等 返回字符串第一个匹配项的索引的下标 3.代码实现 class Solution {public int strStr(String haystack, String needle) {int s1haystack.length();int s2needle.length();//遍历最…

uport1100系列转接头,SZ系列光电编码器RS485通信

安装uport1100系列转接头驱动 &#xff1a;选择对应自己系统内核版本的驱动。否则爆出系统内核过高过低等问题。 查看系统内核版本指令&#xff1a; uname -r #简要 uname -a #详细驱动下载官网&#xff1a; https://www.moxa.com.cn/support/product-support/software-and-…

Java全栈开发面试实战:从基础到微服务架构

Java全栈开发面试实战&#xff1a;从基础到微服务架构 在一次互联网大厂的Java全栈开发岗位面试中&#xff0c;一位名叫李明的28岁程序员&#xff0c;拥有计算机科学与技术本科学历&#xff0c;工作年限为5年。他的主要职责包括设计和实现前后端分离的Web应用、参与微服务架构的…

win10(三)视频剪裁

上传一刻相册&#xff0c;有30M大小限制。这个软件能免费剪裁视频而且支持手机的H.265格式&#xff0c;这个格式目前连potplayer都支持不好。但是配合FFmpeg可以检测并且能按大小&#xff08;或时间&#xff09;剪裁&#xff0c;并上传到一刻相册上播放。 下载FFmpeg的方法&am…

Linux--线程

Linux线程概念 1 什么是线程 • 在⼀个程序⾥的⼀个执⾏路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“⼀个进程内部 的控制序列” • ⼀切进程⾄少都有⼀个执⾏线程 • 线程在进程内部运⾏&#xff0c;本质是在进程地址空间内运⾏ • 在L…

【C++】C++11的包装器:function与bind简介

各位大佬好&#xff0c;我是落羽&#xff01;一个坚持不断学习进步的学生。 如果您觉得我的文章还不错&#xff0c;欢迎多多互三分享交流&#xff0c;一起学习进步&#xff01; 也欢迎关注我的blog主页: 落羽的落羽 文章目录一、function1. 概念2. 用法二、bind1. 概念2. 用法…

MySQL高级特性详解

MySQL高级特性详解 一、自关联查询 概念 自关联查询是指一个表与它自己进行连接的查询。通常用于处理具有层级关系或递归结构的数据。 应用场景 员工与上级关系分类的父子关系地区的层级关系 示例 -- 创建员工表 CREATE TABLE employees (emp_id INT PRIMARY KEY,emp_name VARC…

深度学习——调整学习率

学习率调整方法详解在深度学习训练过程中&#xff0c;学习率&#xff08;Learning Rate, LR&#xff09; 是影响模型收敛速度和效果的关键超参数。学习率过大可能导致训练不稳定、震荡甚至无法收敛&#xff1b;学习率过小又会导致收敛过慢甚至陷入局部最优。因此&#xff0c;如…

Java分页 Element—UI

前端代码 <div class"block"><span class"demonstration">页数较少时的效果</span><el-paginationlayout"prev, pager, next":total"50"></el-pagination> </div>参考Element-UI total:0, form: …

html中列表和表格的使用

列表一般来说只有一列一列的进行使用&#xff0c;是一维的列表分为三种列表形式<!-- 列表标签ul-li:无序列表&#xff0c;必须用 <ul> 当 “容器”&#xff08;代表 “无序列表”&#xff09;&#xff0c;每个条目用 <li> 包起来&#xff08;代表 “列表项”&am…

大学信息查询平台:一个现代化的React教育项目

一 项目简介大学信息查询平台是一个基于React Vite Tailwind CSS构建的现代化Web应用&#xff0c;专门用于查询中国各大高校的详细信息。该项目不仅功能实用&#xff0c;更在用户体验和界面设计上做到了极致。二 核心功能2.1. 智能大学搜索// 搜索功能核心代码 const searchU…

代码随想录算法训练营第六天 - 哈希表2 || 454.四数相加II / 383.赎金信 / 15.三数之和 / 18.四数之和

代码随想录算法训练营第六天 - 哈希表2 || 454.四数相加II / 383.赎金信 / 15.三数之和 / 18.四数之和454.四数相加II解题思路383.赎金信自己解答&#xff1a;代码随想录讲解暴力做法哈希表15.三数之和双指针优化改进18.四数之和自己的解答系统讲解454.四数相加II 文档讲解&…

FPGA实现流水式排序算法

该算法采用双调排序算法&#xff0c;是一种可流水的递推算法&#xff0c;且算法的消耗时长可算&#xff0c;具体细节参考视频&#xff1a; https://www.bilibili.com/video/BV1S3thzWEnh/?spm_id_from333.1387.homepage.video_card.click&vd_source69fb997b62efa60ae1add…