浅析C++模板类型中的原样转发和可变参数的实现

 更新时间:2022年08月09日 15:49:57   作者:恋恋风辰  
可变参数模板(variadic templates)是C++11新增的强大的特性之一,它对模板参数进行了高度泛化,能表示0到任意个数、任意类型的参数,这篇文章主要介绍了C++可变参数模板的展开方式,需要的朋友可以参考下

原样转发的意义

前文我们实现了一个my_move函数,用来模拟stl的move操作,实现去引用的功能。其内部的原理就是通过remove_reference实现去引用操作。

有时我们也需要保留原类型的左值或者右值属性,进行原样转发,此时就要用forward实现转发功能。

我们先定义一个模板函数

template <typename F, typename T1, typename T2>
void flip1(F f, T1 t1, T2 t2)
{
    f(t2, t1);
}

flip1内部调用了函数f

我们写一个函数测试

void ftemp(int v1, int &v2)
{
    cout << v1 << " " << ++v2 << endl;
}
void use_ftemp(){
    int j = 100;
    int i = 99;
    flip1(ftemp, j, 42);
    cout << "i is " << i << " j is " << j << endl;
}

通过打印发现i和j的值没有变化,因为ftemp的v2参数虽然是引用,但是是flip1的形参t1的引用

t1只是形参,修改t1并不能影响外边的实参j。

想要达到修改实参的目的,需要将flip1的参数修改为引用,我们先实现修改后的版本flip2

template <typename F, typename T1, typename T2>
void flip2(F f, T1 &&t1, T2 &&t2)
{
    f(t2, t1);
}

我们定义了一个flip2函数,t1和t2分别是右值引用类型。接下来用一个测试函数进行测试

int j = 100;
int i = 99;
flip2(ftemp, j, 42);
cout << "i is " << i << " j is " << j << endl;

这次我们发现j被修改了,因为flip2的t1参数类型为T1的右值引用,当把实参j赋值给flip2时,T1变为int&,

t1的类型就是int& &&,通过折叠t1变为int&类型。这样t1就和实参j绑定了,在flip2内部修改t1,就达到了修改j的目的。

但是flip2同样存在一个问题,如果flip2的第一个参数f,如果f是一个接受右值引用参数的函数,会出现编译错误。

为说明这一点,我们实现一个接纳模板参数右值引用类型的函数

void gtemp(int &&i, int &j)
{
    cout << "i is " << i << " j is " << j << endl;
}

此时如果我们将gtemp作为参数传递给flip2会报错

int j = 100;
int i = 99;
// flip2(gtemp, j, 42) 会报错
// 因为42作为右值纯递给flip2,t2会被折叠为int&类型
// t2传递给gtemp第一个参数时,int&&无法绑定int&类型
//flip2(gtemp, i, 42);
cout << "i is " << i << " j is " << j << endl;

当我们将42传递给flip2第二个参数时,T2被实例化为int类型,t2就变为int && 类型,通过折叠t2变为int&类型。

t2作为参数传递给gtemp的第一个参数时会报错,

cannot bind rvalue reference of type ‘int&&’ to lvalue of type ‘int’

因为t2是一个左值,右值无法绑定该左值。

解决的办法就是实现一个flip函数,内部实现对T2,T1类型的原样转发。

template <typename F, typename T1, typename T2>
void flip(F f, T1 &&t1, T2 &&t2)
{
    f(std::forward<T2>(t2), std::forward<T1>(t1));
}

通过forward将t2类型转化为和T2类型一样的类型,也就是int的右值类型,接下来的调用就不会出问题了

void use_ftemp()
{
    int j = 100;
    int i = 99;
    flip(gtemp, i, 42);
    cout << "i is " << i << " j is " << j << endl;
}

模板的可变参数

模板同样支持可变参数

//可变参数的函数模板
template <typename T>
ostream &print(ostream &os, const T &t)
{
    return os << t; //输出最后一个元素
}
template <typename T, typename... Args>
ostream &print(ostream &os, const T &t, const Args &...rest)
{
    os << t << ", ";
    return print(os, rest...);
}

Args是可变的模板参数包, 然后再用Args定义rest变量,这是一个可变参数列表。

我们的模板函数print内部调用stl的print函数,通过对rest…实现展开操作。

调用过程可按如下的方式

void use_printtemp()
{
    int i = 100;
    string s = "hello zack!!!";
    print(cout, i, s, 42);
}

第一次调用print实际是调用的可变参数的print,之后才调用没有可变参数的print函数。

总结

本文介绍了模板类型的原样转发,以及多模板参数列表的使用。

视频链接

源码链接

到此这篇关于浅析C++模板类型中的原样转发和可变参数有什么意义的文章就介绍到这了,更多相关C++原样转发和可变参数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言用函数实现反弹球消砖块

    C语言用函数实现反弹球消砖块

    这篇文章主要为大家详细介绍了C语言用函数实现反弹球消砖块,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • C语言中((type *)0) 和(type *0)区别小结

    C语言中((type *)0) 和(type *0)区别小结

    ((type *)0) 和 (type *0) 在 C 和 C++ 中有不同的含义和用途,本文主要介绍了C语言中((type *)0) 和(type *0)区别,具有一定的参考价值,感兴趣的可以了解一下
    2024-08-08
  • C++中初始化二维数组的几种常见方法

    C++中初始化二维数组的几种常见方法

    本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vector作为动态二维数组的灵活性和应用场景,需要的朋友可以参考下
    2025-04-04
  • C++实战之二进制数据处理与封装

    C++实战之二进制数据处理与封装

    在电脑上一切数据都是通过二进制(0或1)进行存储的,通过多位二进制数据可以进而表示整形、浮点型、字符、字符串等各种基础类型数据或者一些更复杂的数据格式。本文将为大家详细讲讲二进制数据处理与封装,需要的可以参考一下
    2022-08-08
  • C语言实现三子棋游戏的示例代码

    C语言实现三子棋游戏的示例代码

    今天我们将会用C语言实现三子棋。所谓三子棋,就是三行三列的棋盘,玩家可以和电脑下棋,率先连成三个的获胜。话不多说,我们开始吧
    2022-10-10
  • Qt 添加MSVC2017编译器的完整教程(保姆级)

    Qt 添加MSVC2017编译器的完整教程(保姆级)

    本文主要介绍了Qt 添加MSVC2017编译器的完整教程,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-06-06
  • C/C++ Qt ToolBar菜单组件的具体使用

    C/C++ Qt ToolBar菜单组件的具体使用

    ToolBar工具栏在所有窗体应用程序中都广泛被使用,使用ToolBar可以很好的规范菜单功能分类,本文就详细的介绍一下ToolBar组件的应用,感兴趣的可以了解一下
    2021-11-11
  • C++实现商店仓库管理系统

    C++实现商店仓库管理系统

    这篇文章主要为大家详细介绍了C++实现商店仓库管理系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03
  • C/C++中获取重载函数地址的方法

    C/C++中获取重载函数地址的方法

    函数重载是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这 些同名函数的形参列表不同,常用来处理实现功能类似数据类型不同的问题,本文给大家介绍了C/C++中获取重载函数地址的方法,需要的朋友可以参考下
    2024-04-04
  • C语言实现宿舍管理课程设计

    C语言实现宿舍管理课程设计

    这篇文章主要为大家详细介绍了C语言实现宿舍管理课程设计,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-03-03

最新评论