按值传递还是按引用传递

使用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.mzph.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__() 是…