礼泉做网站share群组链接分享

当前位置: 首页 > news >正文

礼泉做网站,share群组链接分享,做淘宝客如何建立网站,建设工程司法解释(二)模板中的右值引用#xff08;万能引用#xff09;、引用折叠与完美转发 文章目录 模板中的右值引用#xff08;万能引用#xff09;、引用折叠与完美转发一、万能引用与引用折叠1. 模板中的右值引用2. 自动类型推导(auto)与万能引用3. 引用折叠与万能引用4. lambda表达式捕…模板中的右值引用万能引用、引用折叠与完美转发 文章目录 模板中的右值引用万能引用、引用折叠与完美转发一、万能引用与引用折叠1. 模板中的右值引用2. 自动类型推导(auto)与万能引用3. 引用折叠与万能引用4. lambda表达式捕获5. 条件转发6. 类型萃取 二、完美转发总结 一、万能引用与引用折叠

  1. 模板中的右值引用 ​ 我们经常听万能引用什么是万能引用与普通的右值引用有什么区别 下面给出一个案例利用模板函数参数中的右值引用来展现万能引用的用途 首先给出一个自定义的类型简易的实现出拷贝构造函数和移动构造函数同时不必关注是否正常实现了功能增加打印信息以便我们可以通过控制台明晰该函数是否被调用何时被调用 class Date { public:Date(int year, int month, int day):_year(year), _month(month), _day(day){cout Date(int year, int month, int day) endl;}Date(const Date d){cout Date(const Date d) endl;}Date(Date d){cout Date(Date d) endl;} private:int _year;int _month;int _day; };接着给出两个测试函数函数参数类型分别常量左值引用和右值引用 // 常量左值引用函数模板 templatetypename T void func1(const T val) {T d(val); } // 万能引用参数函数模板 templatetypename T void func2(T val) {T d(std::forwardT(val));cout val是右值引用 std::is_rvalue_referencedecltype(val)::value endl; }注意上面 func2() 函数中用到的 forward() 相当于 move() 的进阶版可以保证在函数间参数传递时保持原有左/右值类型。 继续给出测试用例 Date d(2000, 1, 2);cout ******左值参数***** endl; func1(d); cout ************************* endl; func2(d);cout ******右值参数***** endl; func1(std::move(d)); cout ************************* endl; func2(std::move(d));运行结果 通过上图我们认识到万能引用在模板中的重要性万能引用是模板中大多数情况保证移动构造函数被正常调用的重要条件。只有参数是万能引用时函数内部才可调用 std::forwardT() 完美转发变量的左右值类型并将保持源类型的变量转发给其他函数。 至于我们为什么称其为“万能引用”虽然常量左值引用 “const T val” 也能兼容接收左值和右值参数但它不支持移动语义因为它不允许修改绑定的对象。因此万能引用更有优势因为它既支持移动语义又能与完美转发结合使用成功传递变量及其类型。 2. 自动类型推导(auto)与万能引用 当使用auto关键字时万能引用可以帮助推导变量的类型。例如auto可以根据初始化表达式是左值还是右值来推导出正确的类型。 void test2() {Date d(2000, 1, 2);auto d1(d);cout d1是左值引用 std::is_lvalue_referencedecltype(d1)::value endl;cout d1是右值引用 std::is_rvalue_referencedecltype(d1)::value endl;auto d2(std::move(d));cout d2是左值引用 std::is_lvalue_referencedecltype(d2)::value endl;cout d2是右值引用 std::is_rvalue_referencedecltype(d2)::value endl; }通过上图我们发现 auto 可以在推导表达式类型时同时推导其左值性或右值性。
  2. 引用折叠与万能引用 引用折叠的规则解决了引用的引用C中不允许的问题。这里是引用折叠的基本规则 如果两个引用中至少一个是左值引用那么结果是左值引用。如果两个引用都是右值引用那么结果是右值引用。 首先给出一个简单的以万能引用为参数的模版函数 templatetypename T void func(T val) {// val 是一个万能引用auto p val;cout 左值引用 std::is_lvalue_referencedecltype(val)::value endl;cout 右值引用 std::is_rvalue_referencedecltype(val)::value endl; }测试函数 void test3() {int x 10;int lx x; // lx 是 x 的左值引用int rx 10; // rx 是一个右值引用func(x); // T 推导为 int因此 val 的类型为 int 折叠为 intcout x是右值引用 std::is_rvalue_referencedecltype(x)::value endl;cout ——————- endl;func(lx); // T 推导为 int因此 val 的类型为 int 折叠为 intcout lx是右值引用 std::is_rvalue_referencedecltype(lx)::value endl;cout ——————- endl;func(rx); // T 推导为 int因此 val 的类型为 int 折叠为 intcout xr是右值引用 std::is_rvalue_referencedecltype(rx)::value endl;cout endl;func(move(x));cout move(x)是右值引用 std::is_rvalue_referencedecltype(move(x))::value endl;cout ——————- endl;func(10); // T 推导为 int因此 val 的类型为 int }运行结果 对上面运行结果部分地方需要着重做出解释 4. lambda表达式捕获 void test4() {auto x 5;auto lambda y std::move(x) mutable {y 2;return y;};cout x endl;cout lambda() endl; }当调用 lambda() 时将输出 7因为 y 被初始化为 5 并且增加了 2。请注意由于 x 被移动到 yx 的值在移动后未定义但通常在实际编译器实现中基本类型的值在移动后保持不变。因此输出 x 的值仍然是 5。
  3. 条件转发 templatetypename T void forwarder(T arg) {if constexpr (std::is_lvalue_reference_vT) {//process(arg); // 处理左值}else {//process(std::move(arg)); // 处理右值} }在这个例子中forwarder 函数使用万能引用 T 来接受任何类型的参数并根据参数的类型来决定调用哪个 process 函数。
  4. 类型萃取 类型萃取Type Traits是模板元编程中的一种技术它允许你在编译时检查类型信息或者修改类型。万能引用可以与类型萃取结合使用以确定传递给模板的参数类型的属性。 templatetypename T void process(T arg) {using Type typename std::remove_referenceT::type;if constexpr (std::is_integral_vType) {// 如果 T 是整数类型//handle_integral(std::forwardT(arg));}else {// 如果 T 不是整数类型//handle_non_integral(std::forwardT(arg));} }在这个例子中process 函数使用类型萃取来移除引用并检查 T 是否为整数类型。然后它使用 std::forward 来保持参数的值类别并将其传递给相应的处理函数。 二、完美转发 ​ 实际上这里才真正提起完美转发这个概念看了本文前面内容也已经大概了解我们这里仅仅将其抽离出来进行特别总结。 首先我们已经知道右值引用变量具有左值属性因为其需要保留可修改性所以自然不能是右值。 其次传递给右值引用类型的参数在传参后会退化为左值。
    所以我们想要在万能引用作参数的函数内部实现分离操作需要对参数的左右值属性进行判断例如 templatetypename T void print_right(T v) {cout right - v endl; } templatetypename T void print_left(const T v) {cout left - v endl; }templatetypename T void do_something(T val) {if (std::is_rvalue_referencedecltype(val)::value) // val是右值{print_right(std::forwardT(val)); // 利用完美转发将退化逆向为初始类型此处判断初始右值也可 move(val)}else // val是左值{print_left(val); // val退化为左值直接传递} }测试函数 void test5() {int x 18;//do_somethingint(x); // 编译报错do_somethingint(x); // 注意这里x是左值所以模板参数为int编译会报错模板参数和函数参数类型需要统一满足引用折叠do_somethingint(10); }注意上面测试代码的案例也是引用折叠的重要体现 对于完美转发 forward(val) 中 val 的属性可能是左值也可能是右值 当 val 是左值时forward 对左值 val 不做处理当 val 是右值时forward 的作用相当于 move(val)。 总结 ​ 本文被三个词语所贯穿万能引用、引用折叠、完美转发。这三个概念之间的联系紧密以及使用场景高度重合正是因为C11中提出如此富有意义的新概念极大地方便了我们重构代码理想高效地编码实现功能。