当前位置: 首页> 健康> 美食 > 商标买卖_供应商管理办法_如何网上免费打广告_广州网络营销运营

商标买卖_供应商管理办法_如何网上免费打广告_广州网络营销运营

时间:2025/7/11 8:02:20来源:https://blog.csdn.net/djb100316878/article/details/146974403 浏览次数:0次
商标买卖_供应商管理办法_如何网上免费打广告_广州网络营销运营

今天,来扒一扒 C++ 里容易让人理解混淆的 && 符号,特别是它在“万能引用”(Universal Reference,现在更推荐叫 Forwarding Reference,转发引用)和“右值引用”(Rvalue Reference)这两个身份间反复横跳的骚操作。

第一:右值引用 - "一次性"道具的专属接收器

在 C++11 这个伟大的版本问世之前,我们 C++ 程序员过着相对“朴素”的生活。

对象要么是“有名有姓”的左值(Lvalue),比如 int a = 10; 里的 a,你可以反复用它的名字找到它,给它赋值,取它的地址,就像你家养的那只可以撸可以喂、随时能找到的猫。

要么就是昙花一现的右值(Rvalue),比如 10a + b 的结果、函数返回的临时对象 getString()。它们就像你在路边捡到的、用完就可能消失的优惠券,或者外卖送的一次性筷子,用完就扔,通常没有名字,也不能(或者不应该)对它们进行修改。

拷贝这些“一次性用品”往往是浪费的。比如你写 std::string s = getString();,如果 getString() 返回一个临时的 std::string 对象(右值),老版本的 C++ 会傻乎乎地把这个临时对象里的数据(比如一大段文字)完完整整地复制一份到新的 s 对象里,然后那个临时对象就被销毁了。这就像你点外卖,人家送来一份用精美一次性餐盒装的饭,你非得把它小心翼翼地倒进你自己的碗里,然后把那个还能用的餐盒扔掉… 何必呢?

于是,C++11 带来了右值引用(Rvalue Reference),语法就是 类型&&。它的核心使命只有一个:绑定到右值!

void process_disposable(std::string&& disposable_cup) {// 这里的 disposable_cup 明确表示:我只接收那些“一次性”的 string!std::cout << "Processing the disposable cup's content: " << disposable_cup << std::endl;// 重点来了:我可以“偷”走它的资源!std::string my_permanent_mug = std::move(disposable_cup); // 资源转移,杯子空了std::cout << "Content moved to my mug: " << my_permanent_mug << std::endl;// 注意:disposable_cup 现在可能为空了,不能再依赖它的内容了
}int main() {std::string permanent_bottle = "Water";// process_disposable(permanent_bottle); // 编译错误!人家不要你的“永久水瓶”(左值)process_disposable("Juice"); // OK!"Juice" 是个临时字符串(右值)process_disposable(std::string("Milk")); // OK!std::string("Milk") 创建临时对象(右值)std::string another_bottle = "Soda";// 如果你非要把你的永久水瓶当一次性的给,需要显式“打包”process_disposable(std::move(another_bottle)); // OK!std::move 把它伪装成右值// 但要小心,another_bottle 的内容可能被“偷”走了!return 0;
}

生活案例:右值引用就像是"二手闲置物品接收点"

想象一下,你家小区门口有个牌子写着:“闲置物品(即将丢弃)接收点,联系人:张三 &&”。

  • 规则:这个接收点(张三&&)只接收那些你明确表示“我不要了,准备扔了”的东西(右值)。比如你刚喝完的一次性饮料瓶、过期的杂志、穿不了的旧衣服。

  • 好处:张三(右值引用)拿到这些东西后,可以“废物利用”,比如把瓶子拿去卖钱,把杂志内容剪下来做手工,把旧衣服拆了做抹布(对应 C++ 的移动语义 ,转移资源而不是拷贝)。他知道这些东西的原主人不打算再要了,所以可以大胆地“破坏性”使用。

  • 限制:你不能把你家祖传的、还在用的电视机(左值 my_tv)直接搬过去给张三,他会拒收,说:“嘿!这玩意儿你还用呢,我不能收!”. 除非你郑重声明:“这电视我确实不要了!给你了!”,相当于你对电视机用了 std::move(my_tv),把它“标记”为可以被接收的状态。但一旦你这么做了,就别指望回家还能看这台电视了,它的“灵魂”(资源)可能已经被张三搬走了。

总结一下右值引用

  • 语法:类型&& (在类型 不是 模板参数推导上下文,或者 auto&& 推导上下文时)

  • 作用:专门绑定到右值。

  • 目的:实现移动语义,避免不必要的拷贝,提升性能。就像那个只收闲置品的张三,高效利用资源。

第二:万能引用 - "百变星君"的身份魔法

再说到万能引用(Universal Reference)!它是 C++ 界的“百变星君”,它也用 && 符号,但玩法完全不同!

万能引用,由 Scott Meyers 大神提出,虽然现在官方和很多开发者更倾向于叫它 转发引用(Forwarding Reference),但“万能引用”这个名字实在太形象了,我们先用着,后面再强调它的核心使命是“转发”。

万能引用只在特定的上下文中出现,满足以下两个条件时,T&& 才不是右值引用,而是万能引用:

  1. 发生在模板类型推导中:函数模板的参数类型是 T&&,其中 T 是需要推导的模板参数。

    template<typename T>
    void magic_box(T&& item) { // <--- 这里的 T&& 就是万能引用!// ... 魔法操作 ...
    }
    
  2. 发生在 auto类型推导中:变量声明使用 auto&&

    auto&& magic_variable = some_expression; // <--- 这里的 auto&& 也是万能引用!
    

关键区别:看到没?类型推导!这就是区分它是“专一的右值引用”还是“百变的万能引用”的唯一标准!

如果 && 所在的类型涉及到编译器的类型推导(typename Tauto),那它就是万能引用;

如果类型是写死的(比如 std::string&&),那就是右值引用。

那么,“万能”体现在哪里呢?

万能引用之所以“万能”,是因为它既可以绑定到左值,也可以绑定到右值!简直是通吃!

  • 当你传递一个左值给万能引用时,模板参数 T 会被推导为左值引用类型(例如 int&),然后根据 C++ 的引用折叠规则,T&& (即 int& &&)会折叠成 int&(左值引用)。

  • 当你传递一个右值给万能引用时,模板参数 T 会被推导为普通类型(例如 int),T&& (即 int&&)就保持为 int&&(右值引用)。

  • 记住:推导的结果只有两个:左值引用或者普通类型,没有右值引用

转发引用这套特殊的类型推导规则总结:

  • 规则 1:如果传递给 T&& 的实参是一个左值 (Lvalue) ,类型为 U,那么 T 会被推导为 U& (左值引用类型)。

  • 规则 2:如果传递给 T&& 的实参是一个右值 (Rvalue) ,类型为 U,那么 T 会被推导为 U (原始非引用类型)。

引用折叠规则小抄(记住这个,你就掌握了万能引用的核心秘密):

  • T& & -> T& (左引用 的 左引用 还是 左引用)

  • T& && -> T& (左引用 的 右引用 变成 左引用)

  • T&& & -> T& (右引用 的 左引用 变成 左引用)

  • T&& && -> T&& (右引用 的 右引用 还是 右引用)

简单记:
只要有 &(左值引用)参与折叠,结果就是 &(左值引用)。
只有 &&&&碰头,结果才是 &&(右值引用)。

推导是针对 T 进行,引用折叠是针对参数进行,先进行推导,然后拿推导出的 T 对参数进行引用折叠,得到最后的值

看个例子:

#include <iostream>
#include <string>
#include <utility> // 为了 std::forwardvoid process_further(const std::string& s) {std::cout << "Processing as LValue (const ref): " << s << std::endl;
}void process_further(std::string&& s) {std::cout << "Processing as RValue (move): " << s << std::endl;// 可以在这里移动资源 s
}template<typename T>
void magic_box(T&& item) {std::cout << "Inside magic_box: ";// 仅仅打印类型不够直观,我们后面会看怎么用它// 关键点:无论传入的是左值还是右值,item 在 magic_box 函数内部,// 因为它有名字了,所以它本身是一个左值!// just_print(item); // 如果直接传递 item,总是传递左值// 正确的做法是“完美转发”!process_further(std::forward<T>(item));
}int main() {std::string lv_string = "I am an LValue";magic_box(lv_string); // 传入左值magic_box("I am an RValue"); // 传入右值 (字符串字面量转临时 string)magic_box(std::string("Another RValue")); // 传入右值 (临时 string 对象)std::string another_lv = "One more LValue";magic_box(std::move(another_lv)); // 传入被 std::move 转换的右值return 0;
}

生活案例:万能引用就像是“万能快递代收点”

想象一下,你家小区新开了一个快递代收点,招牌是:“快递代收,联系人:李四 <模板 T> &&”。

  • 规则

这个李四(T&&)非常灵活,不管是是否保价(保价:左值,普通:右值)的快递(T),他都能代收。

  • 怎么做到的?

当你送来一个保价包裹(左值 valuable_package)时,李四心里会记下:“哦,这是个保价物品(T 推导为 Package&),我得按保价物品(Package&)的方式保管。”

当你送来一个普通包裹(右值 create_temp_package())时,他记下:“嗯,这是个普通件(T 推导为 Package),按普通件(Package&&)处理就行。” (这就是类型推导 + 引用折叠)

  • 核心价值(即将引出完美转发)

李四代收了快递后,他的工作还没完。他最终要把快递交给你(或者你指定的下一个人)。这时,他必须 原封不动地 告诉你这个快递 最初 是个保价件还是普通件。他不能把所有收到的快递都当成普通件(就像函数内部参数 item 总是左值一样),也不能都当保价件。他需要一个方法来“恢复”快递的原始属性。

第三:完美转发 - “信使”的神圣使命

我们从上面的 magic_box 例子看到,万能引用 T&& item 虽然能接收左值和右值,但在函数 magic_box 内部,item 这个参数本身,因为它有了名字,就变成了一个左值!

这就带来一个问题:如果 magic_box 的目的是要把接收到的 item 原封不动地(保持其原始的左值或右值属性)传递给另一个函数(比如上面例子中的 process_further),直接传递 item 就不行了,因为 item 已经是左值了。

这就是 完美转发(Perfect Forwarding) 的用武之地,而实现它的工具就是 std::forward

std::forward<T>(item) 的作用就是:根据模板参数 T 被推导出的原始类型(是 int& 还是 int),将左值 item 转换回它对应的原始值类别(value category)。

  • 如果当初传入 magic_box 的是左值,T 推导为 Type&std::forward<Type&>(item) 会返回一个左值引用。

  • 如果当初传入 magic_box 的是右值,T 推导为 Typestd::forward<Type>(item) 会返回一个右值引用。

所以,万能引用的标准用法几乎总是和 std::forward 成对出现,像这样:

template<typename T>
void forwarding_function(T&& arg) {// ... 可能有一些自己的逻辑 ...// 把 arg 完美转发给下一个函数callee_function(std::forward<T>(arg)); 
}

生活案例:“万能快递代收点”的终极形态

李四(T&&)的代收点现在升级了:

  1. 接收:他能接收任何类型的快递(万能引用 T&&),并根据快递是保价(左值)还是普通(右值)在小本本上记录下原始类型(模板推导 TType&Type)。
  • 内部处理:在他代收点内部,所有快递暂时都放在一个“已接收”区域(参数 item 成为左值)。
  1. 转发:当他要把快递交给最终收件人或下一站时,他会查小本本(看 T 的类型),然后使用一个特殊的“转发标签”(std::forward<T>),告诉对方:“这个快递,请按照它原本是保价还是普通的属性来处理!”(完美转发)。

这样,无论快递经历了多少次代收(函数调用链),只要每一站都使用万能引用和完美转发,快递的原始“身份”(左值/右值属性)就能一直保持下去,直到它被最终消费(比如被移动构造或拷贝构造)。

第四:总结与区分

特性右值引用 (Rvalue Reference)万能引用/转发引用 (Universal/Forwarding Reference)
语法形式类型&&T&& (T 是模板参数) 或 auto&&
关键条件类型是确定的,没有类型推导参与必须发生在模板类型推导或auto&&推导上下文中
绑定对象只能绑定到右值既能绑定到左值,也能绑定到右值
推导行为无类型推导传入左值时,T推导为Type&;传入右值时,T推导为Type
引用折叠不涉及(因为类型固定)核心机制Type& && -> Type&, Type&& && -> Type&&
主要目的实现移动语义,优化资源转移实现完美转发,保持值类别在函数调用链中传递
常用搭档std::move (用于将左值转为右值以供绑定)std::forward (用于在函数内部恢复原始值类别进行转发)
生活类比闲置物品接收点(只收不要的)万能快递代收点(啥都收,且能保持原始状态转发)

如何一眼区分?

记住这个口诀:

模板推导 或 auto, && 变身万能佬;类型写死 不推导, &&就是右值宝。

当看到 T&&auto&& 时,问自己:“这里的 Tauto 是不是正在被编译器推导出来?” 如果是,恭喜,你遇到了“百变星君”万能引用。如果不是,比如 void func(std::string&& s); 或者 int main() { Widget&& w = createWidget(); },那它就是“专一”的右值引用。

记住:

右值引用&& 是 C++11 的性能优化利器,专门处理“一次性用品”,配合 std::move 实现高效的资源转移。

万能引用/转发引用&& 是泛型编程和完美转发的核心,它像个变色龙,能适应并保持参数的原始“价值”,配合 std::forward 确保信息无损传递。

关键字:商标买卖_供应商管理办法_如何网上免费打广告_广州网络营销运营

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: