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

详解c++中万能引用、完美转发、类型推导

值继续看这章之前建议先了解右值的概念,可以看c++详解移动语义,右值引用与move函数的作用,noexcept关键字在移动构造与移动赋值运算符中的重要性-CSDN博客右详细介绍右值

万能引用

万能引用是Scott Meyers提出的术语,指能够根据传入参数类型自动推导为左值引用或右值引用的引用类型。其主要有函数模板参数中的T&&auto&&变量声明两种形式,想要实现万能引用类型必须需要进行推导,并且不能有const修饰符或被容器包装

void right_or_left(int&& a){
    std::cout << "right(int&& a) called" << std::endl;
}
void right_or_left(int& a){
    std::cout << "left(int& a) called" << std::endl;
}
template<typename T>
void func1(T&& t){
    std::cout << t << std::endl;
    right_or_left(std::forward<T>(t));
}
int main() {
    int i = 43;
    func1(1);
    func1(i);
    i = 111;
    func1(std::move(i));
    return 0;
}

// 输出
1
right(int&& a) called
43
left(int& a) called
111
right(int&& a) called

template<typename T>
void f1(T&& param); // 万能引用

template<typename T>
void f2(std::vector<T>&& param); // 普通右值引用

为什么万能引用既可以是左值引用,也可以是右值引用呢?这就涉及到 C++ 的引用折叠语法了。引用分为左值引用 T& 和右值引用 T&& 两种,那么将这两种引用进行排列组合,就会有四种情况。所有的引用折叠最终都代表一个引用,要么是左值引用,要么是右值引用。
规 则就是:如果任一引用为左值引用,则结果为左值引用;否则,结果为右值引用。

  • T& & → T& (左值引用的左值引用折叠为左值引用)
  • T& && → T& (左值引用的右值引用折叠为左值引用)
  • T&& & → T& (右值引用的左值引用折叠为左值引用)
  • T&& && → T&& (右值引用的右值引用折叠为右值引用)

完美转发

示例中还使用了完美转发的概念,完美转发是指在函数模板中将参数以原始类型转发给另一个函数,保持其值类别(左值/右值)不变。下面是完美转发的源码:

template <class _Ty>
constexpr _Ty&& forward(remove_reference_t<_Ty>& _Arg) noexcept {
    return static_cast<_Ty&&>(_Arg);
}

template <class _Ty>
constexpr _Ty&& forward(remove_reference_t<_Ty>&& _Arg) noexcept {
    static_assert(!is_lvalue_reference_v<_Ty>, "bad forward call");
    return static_cast<_Ty&&>(_Arg);
}

完美转发的实现中大量使用了折叠引用的概念,forward的参数使用remove_reference_t<_Ty>&remove_reference_t<_Ty>&&的设计,这是为了为不同类型的参数设置不同的处理规制,在右值的实现中static_assert的作用是,防止将左值传递给右值版本的 forward。当 _Ty 是左值引用时,触发静态断言。

  • 当外部传入左值时
    • 假设调用 std::forward<int&>(arg)
      • _Ty 被显式指定为 int&(左值引用)。
      • _Ty&& 展开为 int& &&,根据引用折叠规则,折叠为 int&
      • 因此,static_cast<int&>(arg) 将左值 arg 转换为左值引用,不改变值类别
      • 返回类型 _Ty&& 折叠为 int&,最终返回左值引用。
  • 当外部传入右值时
    • 假设调用 std::forward<int>(arg) 或 std::forward<int&&>(arg)
      • _Ty 被推导为 int 或显式指定为 int&&(右值引用)。
      • _Ty&& 展开为 int&&(若 _Ty 是 int)或 int&& && → int&&(若 _Ty 是 int&&)。
      • static_cast<int&&>(arg) 将左值 arg 强制转换为右值引用,允许后续的移动操作。
      • 返回类型 _Ty&& 为 int&&,最终返回右值引用。

类型推导

auto根据初始化表达式推导变量类型,但会忽略引用和顶层const

int x = 10;
const int cx = x;
int& rx = x;

auto a1 = x;     // a1 → int
auto a2 = cx;    // a2 → int(忽略顶层const)
auto a3 = rx;    // a3 → int(忽略引用)
auto a4 = &x;    // a4 → int*(指针保留)

decltype(expr)根据表达式的类型和值类别推导类型,保留所有类型信息(包括引用和const)。

  • expr变量名(非表达式),decltype返回该变量的声明类型(保留引用和const)。
  • expr表达式decltype返回表达式结果的类型,并根据值类别添加引用:
    • 左值表达式 → T&
    • 右值表达式 → T
int x = 10;
const int cx = x;
int& rx = x;

decltype(x) d1 = x;     // d1 → int
decltype(cx) d2 = x;    // d2 → const int
decltype(rx) d3 = x;    // d3 → int&
decltype(x + 0) d4 = x; // d4 → int(表达式是右值)
decltype((x)) d5 = x;   // d5 → int&(括号使表达式变为左值)

相关文章:

  • ubuntu24.04.2 NVIDIA GeForce RTX 4060笔记本安装驱动
  • TCP 的 time_wait 有什么作用
  • Zookeeper特性与节点数据类型
  • 【模板】最小生成树
  • docker-Dify外接Fastgpt知识库
  • img 的 onerror属性
  • 实战打靶集锦-33-Bottleneck
  • CD19.【C++ Dev】类和对象(10) 日期类对象的成员函数(日期+天数)
  • django orm的优缺点
  • 数据结构与算法——顺序表的实现以及增、插、删、查、印、毁
  • MySQL-- 多表查询的分类,SQL92与SQL99,7种JOIN的实现,SQL99语法的新特性
  • Postman 全局 Header 如何设置?全局设置了解一下
  • 接口用例设计原则
  • 旋转变换原理
  • 养老更安心!智绅科技“智慧”养老系统,智在何处?
  • A SAM-guided Two-stream Lightweight Model for AnomalyDetection
  • springBoot统一响应类型3.3版本
  • 4、网工软考—VLAN配置—hybird配置
  • 以科技赋能,炫我云渲染受邀参加中关村文化科技融合影视精品创作研讨会!
  • 《白帽子讲 Web 安全》之跨站请求伪造
  • 音著协宣布胜诉,虎牙公司终审被判侵权
  • 中美贸易代表会谈后是否已确定下一次会谈?外交部回应
  • 特朗普与普京通话前夕,英美法德意领导人通话讨论俄乌问题
  • 首映|《星际宝贝史迪奇》真人电影,不变的“欧哈纳”
  • 机器人为啥热衷“搞体育”,经济日报:是向加速融入日常生活发起的冲锋
  • 俄乌直接谈判结束