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

C++ 模板进阶知识——万能引用

目录

  • C++ 模板进阶知识——万能引用
    • 1. 类型区别的基本含义
    • 2. 基本认识
    • 3. 判断题
    • 4. 万能引用的示例
    • 5. 万能引用资格的剥夺与辨认
      • 5.1 剥夺
      • 5.2 辨认
    • 总结

C++ 模板进阶知识——万能引用

万能引用是C++11中引入的一个高级特性,它允许函数模板参数根据传入的实参自动成为左值引用或右值引用。这一特性极大地增强了模板函数的灵活性和通用性,使得编写可同时处理左值和右值的通用代码成为可能。

1. 类型区别的基本含义

看看下面这行代码:

void func(const int &abc){}

如果问:abc是什么类型?你可以脱口而出:const int &类型。这没错,因为在代码中写着呢!

现在,把这个func()函数改造成一个函数模板:

template <typename T> 
void func(const T &abc) { }

main()函数中添加代码调用一下:

func(10);

现在问题来了:T是什么类型?abc是什么类型?

答案是:

  1. T的类型是int;
  2. abc的类型是const int &。

通过观察,T的类型之所以是int,是因为进行函数调用的时候给的参数是10。这是对的,但不全面。T的类型不仅取决于调用时传入的参数,还取决于abc的声明方式(即const T &)。这种类型推导是理解万能引用的关键。接下来,我们探讨当abc的类型为万能引用时如何对T的类型产生影响。

2. 基本认识

Universal Reference(万能引用)后来被称为Forwarding Reference(转发引用)。万能引用是C++11引入的一个概念,它可以根据上下文自动成为左值引用或右值引用。万能引用是一种类型,与int类型类似。右值引用用&&符号表示,主要绑定到右值上,如:

int &&rv = 1000;

来看一个例子:

void myfunc(int &&tmprv) 
{
    std::cout << tmprv << std::endl;
}

main()函数中添加代码:

myfunc(10); // 正确,右值作为实参 
int i = 100; 
myfunc(i); // 错误,右值引用不能绑定左值

myfunc()函数改造成函数模板:

template <typename T> 
void myfunc(T&& tmprv)
{ 
    std::cout << tmprv << std::endl; 
}

编译后发现myfunc(i);不再报错。原因是这里的tmprv既能接受左值,也能接受右值,这就是万能引用。

万能引用存在两种语境:

  1. 必须是函数模板;
  2. 必须发生模板类型推断,并且函数模板形参形如T&&。

万能引用的格式为T&&,它与右值引用形式相同,但解释不同。右值引用作为函数形参时,实参必须传递右值;万能引用则可以绑定左值或右值。因此,万能引用更灵活,它可以变成左值引用或右值引用。

3. 判断题

判断以下参数类型是右值引用还是万能引用:

  1. void func(int &&param){...}
    • 右值引用,因为func()不是函数模板。
  2. template<typename T> void func(T&& tmpvalue) {...}
    • 万能引用。
  3. template<typename T> void func (std::vector<T>&&param) {...}
    • 右值引用。虽然有T&&,但T被嵌套在vector中,失去了直接的类型推导,因此不是万能引用。

4. 万能引用的示例

template <typename T>
void myfunc(T&& tmprv) 
{
    tmprv = 12; // 不管tmprv的类型是左值引用还是右值引用,都可以给tmprv赋值
    std::cout << tmprv << std::endl;
}

int main()
{
    int i = 100; 
    myfunc(i); // 左值被传递,因此tmprv是左值引用,执行完毕后i值变成12
    
    i = 200;
    myfunc(std::move(i)); // 右值被传递,因此tmprv是右值引用,执行完毕后i值变成12
}

通过以上内容,理解了万能引用的概念和应用。万能引用不仅与右值引用相似,但其存在的场景要求T是类型模板参数,并且后面跟随&&。万能引用灵活且强大,可以根据实际情况绑定到左值或右值。

5. 万能引用资格的剥夺与辨认

万能引用(Forwarding Reference)是一个强大的特性,可以在不同的上下文中表现为左值引用或右值引用。然而,某些情况下,一个本应是万能引用的表达式会失去这个资格。理解何时发生这种剥夺以及如何辨认万能引用,对于高效利用C++的模板编程至关重要。

5.1 剥夺

万能引用的资格可以被以下几种情况剥夺:

  1. 类型说明符的存在

    • 如果在类型声明中使用了constvolatile等类型修饰符,那么这个引用将不再是万能引用。例如,const T&&不是万能引用,而是一个常量右值引用。

    • template<typename T>
      void func(const T&& param) {} // 这里的param是常量右值引用,不是万能引用
      
  2. 类型不直接为T&&

    • 如果类型通过别名(typedef或using)间接定义,那么它不再是万能引用。例如:
      template<typename T>
      using myType = T&&;
      template<typename T>
      void func(myType<T> param) {} // 这里的param不是万能引用
      
  3. 数组或函数类型

    • 如果T是数组或函数类型,那么即使使用了T&&的形式,也不是万能引用。

    • template<typename T>
      void func(T&& param) {}
      // 如果T是数组类型 int arr[10]; 则func(arr)中的param不是万能引用
      // 如果T是函数类型 void f(); 则func(f)中的param不是万能引用
      
  4. 模板实例化时T已确定

    • 如果在模板实例化时类型T已经明确,则T&&不再具备万能引用的特性,而是变成了普通的右值引用。

    • template<typename T>
      void func(T&& param) {}
      int main() 
      {
          int a = 10;
          func<int&>(a);  // 在这里,T被显式指定为int&,所以T&&变为int& &&,即int&
      }
      

5.2 辨认

要辨认一个引用是否为万能引用,可以遵循以下规则:

  1. 检查是否在函数模板中

    • 确保正在查看的代码位于模板函数中,且涉及类型推断。
  2. 直接形式为T&&

    • 引用必须直接声明为T&&,其中T是模板参数,并且没有任何修饰(如const或volatile)。
  3. 无类型别名

    • 确保T&&没有通过类型别名定义。直接使用原始形式。
  4. 模板类型参数推断参与

    • 确保在函数调用时存在模板类型参数的推断。如果模板参数在函数调用前已确定,那么该引用就可能不是万能引用。

下面是一些例子来帮助辨认:

template<typename T>
void example1(T&& arg) { }  // 万能引用

template<typename T>
void example2(const T&& arg) { }  // 非万能引用,是const修饰的右值引用

template<typename T>
using RefType = T&&;
template<typename T>
void example3(RefType<T> arg) { }  // 非万能引用,使用了类型别名

int main() {
    int a = 10;
    example1(a);  // T被推导为int&
    example1(10); // T被推导为int
}

总结

理解万能引用的关键在于:它依赖于类型推导,可以绑定到左值或右值,并且最终会根据传入的参数类型,变成左值引用或右值引用。


http://www.kler.cn/a/292400.html

相关文章:

  • 微服务中的技术使用与搭配:如何选择合适的工具构建高效的微服务架构
  • arkUI:水果选择与管理:基于 ArkUI 的长按编辑功能实现
  • Python期末复习 | 列表、元组、字典、集合与字符串 | 代码演示
  • 虎扑APP数据采集:JavaScript与AJAX的结合使用
  • AI风向标|算力与通信的完美融合,SRM6690解锁端侧AI的智能密码
  • Git回到某个分支的某次提交
  • 汽车免拆诊断案例 | 捷豹 E-type怠速不稳定
  • python操作kafka
  • How to apply streaming in azure openai dotnet web application?
  • 抖音无水印视频下载
  • SAP物料分类帐的前台操作
  • Arthas工具使用,分析线上问题好帮手
  • The Prompt Report 1
  • 《挑战极限,畅享精彩 ——韩星地带:逃脱任务 3 震撼来袭》
  • Pr:媒体浏览器
  • 【Linux】解锁系统编程奥秘,高效进程控制的实战技巧
  • 利用Go语言模拟实现Raft协议
  • ElasticSearch-Ingest Pipeline Painless Script
  • 前端代码注释风格 - CSS篇
  • 【Kubernetes知识点问答题】Pod
  • 2024跨境电商卖家寻增量,1688寻源通接口 也想做“主角”
  • 树莓派3B驱动ST7735(内核)(TODO)
  • C语言——插入排序
  • 文本匹配任务(下)
  • 红队攻防 | 利用GitLab nday实现帐户接管
  • 【2024数模国赛题目解析丨免费分享】