c++11学习笔记(5)- 引用折叠和完美转发

原创

C++通过引入一条所谓“引用折叠”的语言规则,并结合模板推导来完成完美转发的。

首先看一个例子:

template<typename T>
void testFunc(T& r)
{
    
}

当我们给函数传入引用时,如果我们对函数 testFunc 传入一个非引用类型的数据,那么形参中r的类型时什么呢?传入一个右值引用,形参r中的类型是什么呢?如果函数参数声明的类型是右值引用,传入左值引用结果又会怎样呢?

模板对类型的推到规则和简单,当转发函数的实参是类型T的左值引用,那么模板参数被推导为T&类型;如果转发函数的实参是类型T的右值引用,那么模板参数被推导为T&&类型。

具体引用折叠可以参照下表:

函数参数声明的类型传入实参的类型r的类型
T&TRr&
T&TR&r&
T&TR&&r&
T&&TRr&&
T&&TR&r&
T&&TR&&r&&

从上表可以看出无论传入什么类型,右值引用都可以保持跟出入的实参类型保持不变。
所以,我们可以这么写完美转发的模板:

template<typename T>
void perfectForward(T&& t)
{
    func(std::forward<T>(t));
}
  • 当传入的为一个const T类型实参时,如果不适用右值引用,则需要声明一个const T的函数版本,因为const T&无法转换为T&,由于使用的右值引用我们就可以完全不用声明带有const版本的转发函数。
  • 使用std::forward是为了保证函数可以调用正确形参形式的函数。

下面是一个不带std::forward的示例:

#include <iostream>
#include <stdlib.h>
#include <type_traits>

void func(int& t)
{
    std::cout << "func(int& t)" << std::endl;
}

void func(const int& t)
{
    std::cout << "func(const int& t)" << std::endl;
}

void func(const int&& t)
{
    std::cout << "func(const int&& t)" << std::endl;
}

template<typename T>
void perfectForward(T&& t)
{
    //func(std::forward<T>(t));
    func(t);
}

int main(int argc, char** argv)
{
    int a = 0;
    perfectForward(a);

    const int b = 20;
    perfectForward(b);

    perfectForward(std::move(a));

    system("pause");
    return 0;
}

函数的运行结果为:
func(int& t)
func(const int& t)
func(int& t)

但是我们想让调用函数 perfectForward(10); 的时候调用函数 void func(const int&& t) 因为std::move(a)是一个右值引用。而由于引用折叠的作用,当传入一个右值到形参 T& 中会被认为推导为左值。因此,需要是使用std::forwardstd::forward的作用就是强制转化为实际类型的值作为形参。
更改为std::forward 后,运行效果为:
func(int& t)
func(const int& t)
func(const int&& t)

不会飞的纸飞机
扫一扫二维码,了解我的更多动态。

下一篇文章:c++11学习笔记(6)- 智能指针