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

C++11中的完美转发

C++11中的完美转发

在讨论引用折叠这个话题之前,先回顾一下C++11中的引用,

在C++11中引用有4种:非常量左值引用非常量右值引用常量左值引用常量右值引用。其中常量右值引用没有应用价值,所以我们不考虑。

  1. 非常量左值引用只能绑定非常量左值
  2. 非常量右值引用只能绑定非常量右值
  3. 常量左值引用又称万能引用,他能绑定所有值,但是不能修改被绑定的值
  4. 常量右值引用能绑定右值,但是不能修改被绑定的值(没有实用价值)

我们在模板中经常会调用另一个模板函数或者函数,我们希望我们能够在传参时,减少临时对象的产生,即我们总是希望按照引用传参,这时我们就提出了完美转发的概念。

template <class T>
void foo(T t)
{
    run(t);
}

1.引用折叠

typedef const int T;
typedef T& TR;
typedef TR&& TRRR;
TRRR&& a=1;

上面这段代码在C++98中是无法编译的, 而在C++11中却是可以通过的,TRRR &&的类型会被推导为const int &

下面给出具体的折叠规则:

typedef T& TR;
TR v;//T&
TR &v;//T&
TR && v;//T&

typedef T&& TR;
TR v;//T&&
TR & v;//T&
TR && v;//T&&

即左值引用和右值引用在折叠的时候,优先折叠为左值引用

2.完美转发

#include<iostream>
using namespace std;
void run(int &&m)
{
    cout<<"rvalue reference\n";
}
void run(int &m)
{
    cout<<"lvalue reference\n";
}

void run(const int &m)
{
    cout<<"const lvalue reference\n";
}
void run(const int &&m)
{
    cout<<"const rvalue reference\n";
}


template <class T>
void foo( T &&t)
{
    run(static_cast<T&&>(t));
}
int main()
{
    int a;
    foo(a);
    foo(move(a));
    const int b=1;
    foo(b);
    foo(move(b));

}
lvalue reference
rvalue reference
const lvalue reference
const rvalue reference

我们看一下,这个模板函数foo中实现的就是完美转发

template <class T>
void foo( T &&t)
{
    run(static_cast<T&&>(t));
}

如果a是个X类型的左值,那么T会被推导为X&,则模板实例化为

void foo( X& && t)
{
    run(static_cast<X& &&>(t));
}

根据引用折叠理论,上面就会变成左值引用

而如果a是个右值,那么T就会被推导为X,则模板实例化为

void foo( X&& t)
{
    run(static_cast<X&&>(t));
}

所以不管我们传递的是左值还是右值,run中都是按照引用传递的,这就是完美转发。实际上,真正奏效的是static_cast<T &&>()函数,它根本解决的是右值引用本身是个左值,所以得把他转化成右值这件事,
我们会发现,它做的事情和std::move()是一样的事情,但是为了后续区分,我们一般实用forward<T>()函数来代替static_cast<T&&>
注意,move()forward<T>()的区别在于,move是无条件转化为右值,而forward是当且仅当它是右值引用变成的,而且我们要提供模板参数T,用以告知参数的原来类型。

完美转发的应用在于包装函数

#include<iostream>
using namespace std;

void func1(int &&)
{
    cout<<"func1"<<endl;
}
void func2(int &&)
{
    cout<<"func2"<<endl;
}

template <class T,class U>
void foo( T &&t, U& func)
{
    func(forward<T>(t));
}
int main()
{
    foo(1,func1);
    foo(2,func2);
}

上面这种设计就是封装多个函数为一个函数,在C++11中make_pairmake_unique都是用上面这种方式实现的

相关文章:

  • HashTable HashMap ConcurrentHashMap 的介绍以及区别
  • javaEE 初阶 — Socket 套接字与 UDP 数据报套接字编程
  • 使用 TELNET 发送 SMTP 邮件详解
  • 学习笔记:Java 并发编程④
  • 单例的几种写法
  • ARP攻击和欺骗原理讲解
  • 如何安装双系统与多系统(带你快速了解)
  • Java语法核心——面向对象编程
  • 指针与数组
  • qsort函数用法 + 模拟实现qsort函数
  • 1. Spring 基础入门
  • 第十三届蓝桥杯省赛 Java A 组 I 题、Python A 组 I 题、Python B 组 J 题——最优清零方案(AC)
  • python图像处理(图像缩放)
  • 2023需要重点关注的四大AI方向
  • 【BTC】数据结构
  • C语言深度解剖-关键字(2)
  • 【二叉搜索树】BST相关题目
  • 济人药业更新招股书:计划在A股上市,中成药业务收入持续下滑
  • 哪里可以找到电子版的大学课本?
  • 数据结构 第三章 栈和队列(队列)