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
英文地址,請注明出處:http://en.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…