29. 关键字decltype

`decltype` 是 C++11 引入的一种“编译期类型查询”机制,它能根据给定表达式的值类别(lvalue、xvalue、prvalue)精确推导出对应的类型,包括引用和 cv 限定;C++14 进一步引入了 `decltype(auto)`,可完美转发初始化器的值类别;在模板编程中,`decltype` 常用于后置返回类型与完美转发函数,以保持类型与引用语义;但要注意括号的影响及与 `auto` 的细微差别,以避免意外产生引用或丢失限定符。
# decltype 的基本语义
`decltype(expr)` 的类型推导遵循以下规则:

1. **无圆括号的标识表达式**:如果 `expr` 是无括号的变量名或成员访问,结果类型与该实体的声明类型完全一致(不添加引用)[Cppreference](https://en.cppreference.com/w/cpp/language/decltype?utm_source=chatgpt.com)。
2. **带圆括号或其他表达式**:
    - 若 `expr` 是 lvalue,`decltype(expr)` 推导为 `T&`;
    - 若为 xvalue,推导为 `T&&`;
    - 若为 prvalue,推导为 `T` [cppreference.cn](https://cppreference.cn/w/cpp/language/decltype?utm_source=chatgpt.com)。
3. 操作数不求值,类似 `sizeof`,因此诸如 `decltype(i++)` 并不会改变 `i` 值 。
```cpp
auto varname = value; 
decltype(exp) varname [= value];
// varname 表示变量名,value 表示赋给变量的值 
// exp 表示一个表达式,方括号[ ]表示可有可无。
```
# 值类别与括号影响
- **括号陷阱**:
```cpp
int x = 0;
decltype(x) a;    // a 的类型为 int
decltype((x)) b = a; // b 的类型为 int& (因为 (x) 被视作 lvalue 表达式)
```
过度或错误使用括号会导致意外引用类型。
- **函数调用与成员访问**:
```cpp
struct A { double x; };
const A* p = /*...*/;
decltype(p->x) m;    // double
decltype((p->x)) n = m; // const double&(p 是 const A*,成员访问后再加圆括号)
```
成员访问时若带括号则变成 lvalue 引用类型。
# `decltype(auto)`(C++14)

C++14 引入了 `decltype(auto)`,用于变量或函数返回类型占位,可保留初始化表达式的值类别和 cv 限定:
```cpp
int i = 0;
const int ci = 1;
decltype(auto) v1 = i;   // int
decltype(auto) v2 = (i); // int& 
decltype(auto) v3 = ci;  // const int
decltype(auto) v4 = (ci);// const int&
```
在函数模板中,`decltype(auto)` 可用于完美转发返回类型,避免普通 `auto` 丢失引用或限定符。

# 在模板与完美转发中的应用

`decltype` 在泛型编程中特别重要,常配合后置返回类型实现:
```cpp
template<typename T, typename U>
auto add(T&& t, U&& u) -> decltype(t + u) {
    return t + u;
}

template<typename F, typename... Args>
decltype(auto) forward_call(F&& f, Args&&... args) {
    return std::forward<F>(f)(std::forward<Args>(args)...);
}

```
- 后置返回类型使得函数返回类型依赖于模板参数表达式 。
- `forward_call` 示例中,`decltype(auto)` 保留函数返回值的引用语义,实现“完美转发。

# 常见陷阱与最佳实践

1. **避免多余括号**:过度括号会将无引用类型推为引用。
2. **区分 `auto` 与 `decltype(auto)`**:
    - `auto` 总剥离引用与顶层 cv 限定;
        -  auto 必须要根据=右边的初始值 value 推导出变量的类型;所以auto要求变量必须初始化,也就是在定义变量的同时必须给它赋值,不然会编译报错。
    - `decltype(auto)` 保留初始化表达式的值类别与 cv 限定。
        -  decltype 根据 exp 表达式推导出变量的类型,跟=右边的 value 没有关系。 所以decltype 不强制要求初始化。
```cpp
#include <iostream> 
int main() { 
    auto x = 5; //必须初始化,不然会报错,auto是根据初始值推导出变量的类型的 
    decltype(10)y; //可以不初始化,因为是根据()里面的内容推导出来的类型 
    decltype(10)z = 100; //也可以不初始化。
     auto str1 = "decltype_test"; 
     decltype(str1) str2 = "decltype_test2"; 
}
```

- cv 限定符
    - decltype处理顶层const和引用的方式与auto有些不同,如果decltype使用的表达式是个变量,则decltype返回该变量的类型(包括顶层const和引用在内)。:
```cpp
#include <iostream> 
int main(){ 
    const int ci=0, &cj=ci; 
    decltype(ci) x=0; //x的类型是const int; 
    decltype(cj) y=x; //y的类型是const int &,y绑定到变量x; 
    decltype(cj) z; //错误,z是引用,必须初始化; 
}
```

3. **使用 `decltype` 推导容器索引**:  
    推荐使用 `decltype(vec.size()) idx = 0;`,以防容器大小类型变化造成错误或警告。
4. **模板库中使用**:`decltype` 不会实例化表达式,所以可以安全地推导未定义时可能出错的类型,只要表达式本身的类型可在语法层面推导即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芒果敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值