尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
右值和移动
值类别(value categories)lvalue通常可以放在等号左边的表达式, 左值例子变量函数或数据成员的名字返回左值引用的表达式如x, x 1, cout ’ . x 1 和 x返回的都是对x的int. x则返回的是int字符串字面量如 “hello world”rvalue通常只能放在等号右边的表达式, 右值glvaluegeneralized lvalue, 广义左值xvalueexpiring lvalue, 将亡值prvaluepure rvalue, 纯右值值类型(value type)是与引用类型(reference type)相对而言表明一个变量是代表实际数值还是引用另一个数值在C里所有的原生类型枚举结构联合类都代表值类型。只有引用()和指针( * ) 才是引用类型在Java里数字等原生类型是值类型类则属于引用类型在Python里一切类型都是引用类型引用类型(reference value)非常左值引用可以绑定非 const lvalue常左值引用可以绑定 lvalue, xvalue, prvalue非常右值引用可以绑定非 const xvalue, prvalue常右值引用可以绑定 xvalue, prvalue生命周期C的规则是一个临时对象(prvalue)会在包含这个临时对象的完整表达式估值完成后按生成顺序的逆序被销毁除非有生命周期延长发生C对临时对象有特殊的生命周期延长规则如果一个prvalue 被绑定到一个引用上它的生命周期则会延长到跟这个引用变量一样长result r process_shape( circle(), triangle());这条生命周期延长规则只对prvalue有效而对xvalue无效移动的意义右值引用的目的是实现移动而实现移动的意义是减少运行的开销移动案例string result string(Hello, ) name .;C11 以前的执行流程调用构造函数 string(const char*)生成临时对象 1Hello, 复制 1 次调用 operator(const string, const string)生成临时对象 2Hello, 复制 2 次name 复制 1 次调用 operator(const string, const char*)生成对象 3“Hello, 复制 3 次name 复制 2 次”. 复制 1 次假设返回值优化能够生效最佳情况对象 3 可以直接在 result 里构造完成临时对象 2 析构释放指向 string(Hello, ) name 的内存临时对象 1 析构释放指向 string(Hello, ) 的内存C11 以后的执行流程调用构造函数 string(const char*)生成临时对象 1Hello, 复制 1 次调用 operator(string, const string)直接在临时对象 1 上面执行追加操作并把结果移动到临时对象 2name 复制 1 次调用 operator(string, const char*)直接在临时对象 2 上面执行追加操作并把结果移动到 result“.” 复制 1 次临时对象 2 析构内容已经为空不需要释放任何内存临时对象 1 析构内容已经为空不需要释放任何内存如何实现移动除了拷贝构造函数外增加移动构造函数增加swap成员函数实现与另一个对象的快速交换在你的对象的名空间下应当有一个全局的 swap 函数调用成员函数 swap 来实现交换。支持这种用法会方便别人包括你自己在将来在其他对象里包含你的对象并快速实现它们的 swap 函数实现通用的 operator上面各个函数如果不抛异常的话应当标为 noexcept。这对移动构造函数尤为重要noexcept告诉编译器函数不会抛异常有利于编译器对程序做更多的优化以下情形鼓励用noexcept 移动构造函数move constructor、移动分配函数move assignment、析构函数destructor不要返回本地变量的引用在C编程中如果在函数内部返回一个指向本地对象的引用这是一个常见的错误int getLocalVariable() { int num 42; return num; // 错误返回了一个指向本地变量的引用 } int main() { int ref getLocalVariable(); // 错误引用指向已被销毁的本地变量 cout ref endl; // 可能输出任意值因为引用指向已被销毁的内存 }引用坍缩又称“引用折叠”当一个泛型函数模板中的类型参数与实际参数类型相同时编译器会进行类型折叠即将模板中的类型参数替换为实际参数类型从而简化代码。template typename T void process_shape(T shape) { puts(Processing shape); } void foo() { process_shape(circle()); // 调用 process_shape(circle()) process_shape(triangle()); // 调用 process_shape(triangle()) }当我们调用 process_shape(circle()) 和 process_shape(triangle()) 时编译器会根据实际参数类型进行类型折叠即 T 的推导结果分别是 circle 和 triangle在有模板的代码中当类型推导结果相同时编译器会将模板中的类型参数替换为实际参数类型从而简化代码。这个现象在实际编程中经常会遇到完美转发定义一个函数模板该函数模板可以接收任意类型参数然后将参数转发发给其他目标函数且保证目标函数接受的参数其类型与传递给模板函数的类型相同对于一个函数如果形参是右值引用但在函数体内这个“右值引用”实际上是一个左值变量然后函数内再有一个函数传入这个参数那么就会调用对应的左值引用版本而完美转发的意义就相当于做一次类型转换让这个参数保持一开始传入时的左值右值类别移动案例#include string #include ctime #include chrono #include iostream void func1(std::string s) { } void func2(const std::string s) { } void test2() { std::string a hello; auto start std::chrono::system_clock::now(); for (size_t i 0; i 20000000; i) { func1(std::string(hello)); } auto end std::chrono::system_clock::now(); std::chrono::durationdouble elapsed_seconds end - start; std::cout elapsed time: elapsed_seconds.count() s\n; start std::chrono::system_clock::now(); for (size_t i 0; i 20000000; i) { func2(std::string(hello)); } end std::chrono::system_clock::now(); elapsed_seconds end - start; std::cout elapsed time: elapsed_seconds.count() s\n; } // 改进版本 void test3() { std::string a hello; auto start std::chrono::system_clock::now(); for (size_t i 0; i 20000000; i) { func1(a); } auto end std::chrono::system_clock::now(); std::chrono::durationdouble elapsed_seconds end - start; std::cout elapsed time: elapsed_seconds.count() s\n; start std::chrono::system_clock::now(); for (size_t i 0; i 20000000; i) { func2(a); } end std::chrono::system_clock::now(); elapsed_seconds end - start; std::cout elapsed time: elapsed_seconds.count() s\n; } int main() { std::cout ---- test2 测试时间 ---- std::endl; test2(); std::cout ---- test3 测试时间 ---- std::endl; test3(); }结果:---- test2 测试时间 ---- elapsed time: 1.8719s elapsed time: 1.90104s ---- test3 测试时间 ---- elapsed time: 0.424229s elapsed time: 0.0873127s问题: 在test2 case 中为什么func1比func2快func1(string(“func1”))时传入的参数是一个右值.同时在函数调用中临时对象的所有权会被转移给函数的参数s因此会发生一次移动构造操作func2(string(“func2”))时传入的参数也是一个右值虽然函数参数s是通过常量引用传递的但临时对象string(“func2”)可以绑定到常量引用因此不会发生拷贝操作。为什么test3整体时间比test2快?例如func1(a)和func2(a)传入的参数是一个左值因为a是一个已命名的变量它具有持久性并且可以被多次使用. 原因在于使用已命名的变量作为参数时可以直接进行拷贝构造而不需要进行额外的临时对象的创建和销毁参考资料C右值引用
RELATED

相关推荐

AI写作率检测踩坑:我改了3版文档反而把检测率越改越高

AI写作率检测踩坑:我改了3版文档反而把检测率越改越高

上周赶省级技术专项的申报材料,熬了三个大夜交上去,隔天就被行政打回,说提交端口的AI写作率检测直接触发了红色预警,要求3天内提交原创说明加修改后的版本。当时整个人都傻了,7000多字的材料我只是用GPT搭了个三级框架…

📅 2026/9/8 4:01:10
云南财经大学的校园网深澜认证协议

云南财经大学的校园网深澜认证协议

简单介绍了云财关于校园网的加密 免责声明 本文旨在分析校园网认证协议的技术原理,供教育交流与网络优化参考,严禁用于商业用途或任何违反《网络安全法》《著作权法》及学校规定的行为。文中部分描述可能与原有系统关联,相关权利归属开发者所…

📅 2026/9/13 11:02:26
Traccar SMS Gateway vs 传统短信应用:为什么它是物联网设备的最佳选择?

Traccar SMS Gateway vs 传统短信应用:为什么它是物联网设备的最佳选择?

Traccar SMS Gateway vs 传统短信应用:为什么它是物联网设备的最佳选择? 【免费下载链接】traccar-sms-gateway Traccar SMS Gateway for Android 项目地址: https://gitcode.com/gh_mirrors/tr/traccar-sms-gateway Traccar SMS Gateway是一款专…

📅 2026/9/13 18:35:23
MORE NEWS

更多资讯

📰

多阶段鲁棒调度模型:含可再生能源与储能的微电网最优运行的Matlab实现

微电网这个领域,大家一开始做调度优化,十有八九都是从“确定性模型”入门的:把光伏、负荷预测数据固定下来,丢进一个规划模型,出一组漂亮的机组组合和储能充放电计划。可真正对着实际系统跑一圈就会发现,预…

📰

2026年开源机器人遥操作设备:从选型到部署的工程实践指南

1. 从“能遥控”到“好用”的鸿沟:遥操作设备到底卡在哪聊机器人遥操作,很多人第一反应是“不就是个遥控器吗”。真上手做过的人都知道,让机械臂动起来不难,难的是让操作者觉得“这玩意儿跟我的手是一体的”。2026年这个时间节点上…

📰

FreeSQL:Oracle多版本SQL测试的云端解决方案

1. FreeSQL:Oracle多版本特性验证的革命性工具 作为一名长期与Oracle打交道的数据库工程师,我深知多版本环境管理带来的痛苦。每次需要验证不同Oracle版本特性时,要么得在本地部署多个实例,要么就得申请不同的测试服务器——硬件…

📰

MoE稀疏激活架构解析:552B参数如何只花8B推理成本

1. 稀疏激活这件事,到底省在哪1.1 从"全量调用"到"按需唤醒"的账本逻辑先把标题里的数字翻译成人话。552B 参数指的是模型总容量,8B 指的是每次前向推理真正参与计算的参数量。这两个数字差了几十倍,但模型效果却要往 55…

📰

华为OD三轮面试复盘:Java高频真题与核心考点全解析

明白,以下是按照您的要求生成的完整博文内容。这是一篇围绕“华为OD三轮面试复盘:Java开发高频真题核心考点全解析”这一标题展开的深度技术复盘文章,包含面试流程梳理、高频真题拆解、核心考点原理分析、实操心得与避坑指南,全文…

📰

Two-Pass HZB遮挡剔除技术:原理与性能优化实践

1. 技术背景与核心价值在实时渲染领域,遮挡剔除(Occlusion Culling)一直是提升性能的关键技术。传统方案如视锥剔除只能处理相机可见范围内的物体,而Hierarchical Z-Buffer(HZB)技术通过构建深度层级结构&a…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬