按值傳遞還是按引用傳遞

使用std::ref和std::cref

????????從 C++11 開始,可以讓調用者自行決定向函數模板傳遞參數的方式。如果模板參數被聲明成 按值傳遞的,調用者可以使用定義在頭文件<functional>中的 std::ref()和std::cref()將參數按引用傳遞給函數模板,比如:

#include <functional>template <typename T>
void square(T a) { a *= a; }int main(int argc, char **argv)
{double a = 1.414;square(std::ref(a));
}

? ? ? ? 上面的例子似乎不足以體現std::ref或std::cref的重要性,再來看一例子:

#include <functional>
#include <algorithm>
#include <vector>template <typename T>
void add(T &raw, T &sum) {sum += raw;raw += 1;
}int main(int argc, char **argv) {int sum = 0;std::vector<int> v1 {1, 2, 3, 4, 5};std::for_each(v1.begin(), v1.end(), std::bind(add<int>, std::placeholders::_1, sum));return 0;
}

? ? ? ? 執行過程中,會發現sum并沒有按引用傳遞,而是按值傳遞。很明顯,這不符合預期。要解決這個問題,調用std::for_each時,使用std::ref(sum)替換sum即可,如下:

...
std::for_each(v1.begin(), v1.end(), std::bind(add<int>, std::placeholders::_1, std::ref(sum)));
...

? ? ? ? 但使用std::ref時要注意一個問題,其返回的是reference_wrapper,而是不是原始參數類型,因此,下面的代碼是無法通過編譯的:?

//...
template <typename T>
void sub(T a, T b, T r) { r = a - b; }
//...double x = 10.1;double y = 3.5;double z = 0.0;//note: candidate template ignored: deduced conflicting types for parameter 'T' ('double' vs. 'reference_wrapper<double>')sub(x, y, std::ref(z));
//...

處理字符串常量和裸數組

? ? ? ? ?對于字符串常量和裸數組做模板參數:

  • 按值傳遞,參數類型退化成指針
  • 按引用傳遞,參數類型不退化,是指向數組的引用。

? ? ? ? 因此,對于下面的源碼無法編譯通過:

//...
template <typename T>
int compare(const T &lhs, const T &rhs) { return strcmp(lhs, rhs); }int main(int argc, char **argv)
{compare("hello", "abc");return 0;
}
//...

? ? ? ? "hello"和"abc"分別被推導成char[6],和char[4],編譯器找不到合適的函數模板,詳細錯誤如下:

error: no matching function for call to 'compare'compare("hello", "abc");note: candidate template ignored: deduced conflicting types for parameter 'T' ('char[6]' vs. 'char[4]')
int compare(const T &lhs, const T &rhs) { return strcmp(lhs, rhs); }

? ? ? ? 上面的問題可以通過函數重載解決,重載的方法有多種,但普通函數重載最為簡單,也更直接明了。如下:

//通用性太差,無法區分數組變量和普通變量
template <typename T>
int compare(T lhs, T rhs) { return strcmp(lhs, rhs); }//過于繁瑣
template <typename T, size_t L1, size_t L2>
int compare(T (&lhs)[L1], T (&rhs)[L2]) { return strcmp(lhs, rhs); }int compare(const char *lhs, const char *rhs) { return strcmp(lhs, rhs); }

? ? ? ? 總體看來,還是普通函數重載更為簡潔。

處理返回值

? ? ? ? 返回值既可以是值,也可以是引用。以下三種場景,函數通常會返回引用:

  • 需要直接修改返回的對象
  • 提升效率,重新生成一個對象代價較高,如size比較大的容器
  • 為鏈式調用返回一個對象,如std::string的+操作符,重載<<,>>操作符??

? ? ? ? 但在某些情況下,可能更希望函數按值返回。對于普通函數,要做到這點很容易,但對于函數模板,要做到這點,就比較困難。如下面的例子,函數返回的便是引用:

#include <string>
#include <iostream>template <typename T>
T retval(T val)  { return T{val}; }int main(int argc, char **argv)
{std::string str0 = "hello";std::cout << std::is_same<std::string, decltype(retval<std::string &>(str0))>::value << std::endl;std::cout << std::is_same<std::string, std::remove_reference<decltype(retval<std::string &>(str0))>::type>::value << std::endl;return 0;
}

? ? ? ? 如果想函數按值返回,有三種解決方案:

  • 使用std::remove_reference推導返回值類型去掉引用修飾
template <typename T>
typename std::remove_reference<T>::type retval(T val)  { return T{val}; }
  • 使用std::decay推導返回值類型去掉引用修飾
template <typename T>
typename std::decay<T>::type retval(T val)  { return T{val}; }
  • 使用auto推導返回值類型去掉引用修飾。不過該方案直到c++14之后,才能實現去掉引用修飾的目的;c++11要實現這一目的,比較復雜,因為c++11使用auto需要后跟類型推導。實現如下:
//c++11實現返回值無法去掉引用
template <typename T>
auto retval(T val) -> decltype(val)  { return T{val}; }//c++14實現返回值已去掉引用
template <typename T>
auto retval(T val) { return T{val}; }

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/13470.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/13470.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/13470.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

上海初中生古詩文大會倒計時4個月:單選題真題示例和獨家解析

現在距離2024年初中生古詩文大會還有4個多月時間&#xff0c;備考要趁早&#xff0c;因為知識點還是相對比較多的。這些知識點對于初中語文的學習也是很有幫助的。 今天我們繼續來看10道選擇題真題和詳細解析&#xff0c;以下題目截取自我獨家制作的在線真題集&#xff0c;都是…

取名時,要考慮生肖的影響

親愛的寶寶們&#xff0c;又是一年五一小長假&#xff0c;峰民想大家都在休假吧&#xff01;真幸福&#xff01;峰民每天都在工作&#xff0c;幾乎沒有休過假&#xff0c;因為每天全國各地找我們取名改名客戶是絡繹不絕&#xff0c;峰民雖然也很辛勞&#xff0c;但也很有成就感…

Redis:hash數據類型

文章目錄 hash常用命令hsethgethexistshdelhkeyshvalshmget 壓縮hash和string 本篇總結的是&#xff0c;在Redis中的哈希數據類型 hash 在Redis內部本身&#xff0c;其實就是一種鍵值對的結構&#xff0c;而在key-value的value本身&#xff0c;其實也可以是一種哈希結構 而在…

【c++算法篇】滑動窗口

&#x1f525;個人主頁&#xff1a;Quitecoder &#x1f525;專欄&#xff1a;算法筆記倉 目錄 1.長度最小的子數組2.無重復字符的最長子串3.最大連續1的個數 III4.將 x 減到 0 的最小操作數5.水果成籃6.找到字符串中所有字母異位詞7.串聯所有單詞的子串8.最小覆蓋子串 滑動窗…

李宏毅-Self-attention機制詳解

原視頻鏈接&#xff1a;attention 一. 基本問題分析 1. 模型的input 無論是預測視頻觀看人數還是圖像處理&#xff0c;輸入都可以看作是一個向量&#xff0c;輸出是一個數值或類別。然而&#xff0c;若輸入是一系列向量&#xff0c;長度可能會不同&#xff0c;例如把句子里的…

C 深入指針(4)

目錄 一、字符指針變量 1 初始化 2 與字符串數組的區別 二、數組指針變量 1 初始化 2 二維數組傳參本質 三、函數指針變量 1 初始化 2 用法 四、typedef關鍵字 五、函數指針數組 一、字符指針變量 1 初始化 //VS2022 x64 #include <stdio.h> int main() {…

機器人非線性阻抗控制系統

機器人非線性控制系統本質上是一個復雜的控制系統&#xff0c;其狀態變量和輸出變量相對于輸入變量的運動特性不能用線性關系來描述。這種系統的形成基于兩類原因&#xff1a;一是被控系統中包含有不能忽略的非線性因素&#xff0c;二是為提高控制性能或簡化控制系統結構而人為…

人形機器人場景應用全解析,2024睿抗 AI ROBOT創新挑戰賽火熱報名中!

人工智能&#xff08;AI&#xff09;已成為推動科技革命和產業變革的關鍵力量。隨著大模型等AIGC技術的迅猛發展&#xff0c;AI正深刻改變我們的生活并重新定義生產方式。越來越多人期望將AI技術從純粹的思維和計算擴展到與物理世界的互動中&#xff0c;即發展具身智能。 為了推…

探索中國文本到視頻AI模型——Vidu

引言 隨著人工智能技術的不斷進步&#xff0c;我們見證了從文本到視頻內容生成的革命。最近&#xff0c;一個名為Vidu的中國文本到視頻AI模型引起了全球的關注。由清華大學和中國AI初創公司聲書科技聯合開發的Vidu&#xff0c;于2024年4月27日宣布&#xff0c;它聲稱能夠生成高…

測試周期記錄

測試周期是軟件開發生命周期中的一個重要環節&#xff0c;它包括單元測試、集成測試、系統測試和驗收測試等階段。本文將詳細介紹測試周期的各個階段及其重要性&#xff0c;幫助讀者更好地理解測試周期在軟件開發過程中的作用。 一、單元測試 單元測試是測試周期中的第一個階段…

個人工控方面收藏網址記錄(持續更新中)

1、OPC類 OPC Foundation GitHub Downloads - Unified Automation (unified-automation.com) 物聯網IoT協議之OPC UA快速入門教程 | 源碼先生的調試人生 (debugself.com) OPC Servers - OPC UA Migration - 100 Solutions by Matrikon (matrikonopc.com) Prosys OPC UA Simu…

k8s coredns配置

1.coredns可根據集群具體數量修改pod數&#xff0c;官方推薦比例為5/1&#xff0c;即有15臺服務器最好是3個pod。 2.coredns會繼承pod所在主機的dns解析,修改了主機的dns解析之后&#xff0c;coredns有一段時間的緩存&#xff0c;重啟coredns才會在集群內部立刻生效該解析。 …

SpringBoot3集成WebSocket

標簽&#xff1a;WebSocket&#xff0c;Session&#xff0c;Postman。 一、簡介 WebSocket通過一個TCP連接在客戶端和服務器之間建立一個全雙工、雙向的通信通道&#xff0c;使得客戶端和服務器之間的數據交換變得更加簡單&#xff0c;允許服務端主動向客戶端推送數據&#xf…

003_PyCharm的安裝與使用

如果你正在學習PyQt&#xff0c;本系列教程完全可以帶你入門直至入土。 所謂從零開始&#xff0c;就是從軟件安裝、環境配置開始。 不跳過一個細節&#xff0c;不漏掉一行代碼&#xff0c;不省略一個例圖。 IDE 開始學習一個編程語言&#xff0c;我們肯定是首先得安裝好它&…

std::funture和std::promise

#include <iostream> #include <thread> #include <future>void calculateResult(std::promise<int>& promiseObj) {// 模擬耗時計算std::this_thread::sleep_for(std::chrono::seconds(2));// 設置結果到 promise 中promiseObj.set_value(42); }i…

信息系統項目管理師——十大管理過程輸入、工具和技術、輸出(論文篇)二

六、項目風險管理 規劃風險管理 在撰寫關于“規劃風險管理”的論文時&#xff0c;這個過程是項目風險管理的第一步&#xff0c;旨在建立風險管理的框架&#xff0c;為整個項目周期內的風險識別、分析、應對和監控奠定基礎。以下是規劃風險管理過程中可能涉及的輸入、工具和技…

Python學習(五)異常處理

異常概念 異常的捕獲方法 try: f open("D:/abc.txt","r",encoding"UTF-8") except: print("出現異常了&#xff0c;因為文件不存在&#xff0c;我將open的模式&#xff0c;改為w模式去打開") f open("D:/abc.txt&qu…

Python代碼:十、字符串連接

1、題目 小明有兩個最好的朋友&#xff0c;他們的名字分別用input讀入記錄在兩個字符串中&#xff0c;請使用字符串連接&#xff08;&#xff09;幫助牛牛將兩個朋友的名字依次連接在一個字符串中輸出。 2、代碼 import sysstr1 input() str2 input() str3 str1 str2 pr…

從0到1:使用HuggingFace的管線加載Diffusion模型生成第一張圖像!

Hugging Face系列1&#xff1a;詳細剖析Hugging Face網站資源 前言本篇摘要1. Hugging Face Hub三大件1.1 模型1.1.1 模型簡介1.1.2 制作模型卡片1.1.3 模型下載和上傳1.1.4 模型應用 1.2 數據集1.2.1 數據集簡介1.2.2 調用代碼1.2.3 AutoTrain在線微調 1.3 Space應用1.3.1 內容…

理解 Python 中的 `super()` 與 `__init__()` 方法

在 Python 的面向對象編程中&#xff0c;super() 函數和 __init__() 方法是兩個非常重要的概念。它們在類的繼承和初始化過程中扮演著關鍵的角色。本文將深入探討這兩個概念的工作原理&#xff0c;并通過示例代碼來展示它們的使用。 基本原理 __init__() 方法 __init__() 是…