Colderleo's blog Colderleo's blog
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)

Colder Leo

热爱代码的打工人
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)
  • bug定位的一些情形
  • c++性能调优,可能的情况
  • total-编程知识点集锦
  • hpc_common.hpp
  • memory order 内存模型
  • 类型推导之auto-template-decltype
    • auto-template-decltype三者在类型推导时的区别
    • template
      • 1.函数模板参数为普通引用(左值引用)时,整个表达式匹配为左值引用
      • 2.函数模板参数为通用引用"T&& param"时, 整个表达式匹配为左值引用或右值引用
      • 3.函数模板参数为非引用(按值传递)时,
    • auto
    • decltype
  • 完美转发forward源码分析
  • 左值和右值,右值引用、重载 std-move,引用折叠
  • cmake用法
  • alignas、alignof、sizeof实现内存对齐分配
  • 通过宏定义控制debug打印
  • 程序耗时性能测试
  • 线程池开源项目阅读
  • C++类中包含没有默认构造函数的成员
  • C++可变参数模板
  • C++属性继承 public protected private
  • C++智能指针
  • C++导出so的方法,以及extern C 注意事项
  • 四种spin lock
  • condition_variable和unique_lock
  • dpdk、kernel bypass
  • 智能网卡solarflare、Mellanox、X10等
  • 汇编寄存器和常见指令
  • c++ 类的静态成员变量未定义
  • C++获取类成员函数地址
  • preload示例
  • C++异常安全和RAII
  • C++11单例模式
  • C++歪门邪道
  • boost-program-option用法
  • c++17通过模板获取类成员的个数
  • 通过模板实现结构体成员拷贝-按成员名匹配
  • STL学习路径
  • boost库安装使用方法
  • C++文件读写
  • linux下socket通信demo,server和client
  • makefile写法
  • RxCpp
  • C++
gaoliu
2023-04-30
目录

类型推导之auto-template-decltype

# auto-template-decltype三者在类型推导时的区别

参考effective modern c++

# template

template推导T的类型时,有一个规律,就是模板函数传入的参数类型和调用函数时传入的类型匹配一致,并且T尽量简单。

其中左值和左值引用认为是一致的,右值和右值引用认为是一致的。

具体来看三种情况:

# 1.函数模板参数为普通引用(左值引用)时,整个表达式匹配为左值引用

template<typename T>
f(T& param);

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

// 模板函数传入的参数类型为`T& param`, 让其和实际调用时传入的类型一致

f(x); //实际传入的是int,需要让`T& param`匹配int或int&,那么T应是int, 模板传入的`T& param`即为int& param

f(cx); //实际传入的是const int, 需要让`T& param`匹配const int&, 那么T应是const int,模板传入的`T & param`即为const int &

f(rx); //实际传入的是const int&,  需要让`T& param`匹配const int&, 那么T应是const int,模板传入的`T & param`即为const int &
1
2
3
4
5
6
7
8
9
10
11
12
13
14

# 2.函数模板参数为通用引用"T&& param"时, 整个表达式匹配为左值引用或右值引用

  • 当传入左值或左值引用时,该表达式整体匹配为左值引用。
    • 假设传入类型为左值或左值引用(如int或int&),T会被推导为int&, "T&& param"整体即为int& && param, 折叠为int& param
  • 当传入右值时,该表达式整体匹配为右值引用。
    • 假设传入类型为右值int&&,T会被推导为int,整体为int&& param, 无需折叠。
    • 注意右值引用变量本身也是左值,因此如果存在两层函数调用,第一层用了T&&, 且传参为右值,则第一层函数内参数被推导为右值引用,第二层函数传参时需要讲参数声明为T&&,且调用时加上std::forward才能使二层函数内的参数仍推导为右值引用。
    • std::forward的原理是第一层函数调用时如果参数为左值,则第一层的T被推导为左值引用,int&;第一层如果是右值,则T被推导为int。故forward的操作就是将参数做一个static_cast<T &&>(param),那么左值引用的情况int&加上&&仍折叠为左值引用,右值引用的情况第一层T是int,故cast之后仍为右值引用。
template<typename T>
void f(T&& param); //param现在是⼀个通⽤引⽤类型

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

// 模板函数传入的参数类型为`T&& param`, 让其和实际调用时传入的类型一致

f(x); //实际传入的是int, 需要让`T&& param`匹配int或int&, 那么T应是int&, 模板传入的`T&& param`即为`int& && param`, 折叠为int&。  这里如果把T推导为int, 则`T&& param`就是int&& param,与实际传入的左值int不匹配。

f(cx); //实际传入的是const int, 需要让`T&& param`匹配const int, 那么T应是const int&, 模板传入的`T&& param`即为`const int& && param`, 折叠为const int&

f(rx); //实际传入的是const int&, 需要让`T&& param`匹配const int, 那么T应是const int&, 模板传入的`T&& param`即为`const int& && param`, 折叠为const int&

f(27); //实际传入的是int&&, 需要让`T&& param`匹配int&&, 那么T应是int, 模板传入的`T&& param`即为int&& 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 3.函数模板参数为非引用(按值传递)时,

因为是按值传递,所以推导T的类型时会剥离掉 const、volatile、& 等约束

template<typename T>
f(T param);

int x=27; //如之前⼀样
const int cx=x; //如之前⼀样
const int & rx=cx; //如之前⼀样

f(x); //T和param都是int
f(cx); //T和param都是int
f(rx); //T和param都是int

const char* const ptr = " Fun with pointers"; //第二个const表示ptr自身是⼀个常量指针(不能修改ptr指向谁),第一个const表示其指向的对象是常量
// 这种情况下,值传递会把第二个const属性(ptr自身)去掉,留下第一个,即param是const char*
1
2
3
4
5
6
7
8
9
10
11
12
13

# auto

  • auto的作用规则与上面的template基本一样, auto就仿佛是上面的T
  • auto与template的区别在于,auto可以推导出出std::initializer_list, 但是template遇到出std::initializer_list会报错。

比如

int x=27; //如之前⼀样
const int cx=x; //如之前⼀样
const int & rx=cx; //如之前⼀样

auto & a1 = x;  // auto为int, a1为int &
auto && a2 = cx;  //auto 为const int &, a2为 const int &
auto && a3 = 27;  //auto 为int, a3为 int &&

// 让左边和右边的类型一致, 但是上面最后一条规则(按值传递)会自动剥离const和引用,所以:
auto a4 = rx;  //a为int, 剥离了const和&

decltype(auto) a5 = rx; //这是不剥离的方式,a5为const int &, 

1
2
3
4
5
6
7
8
9
10
11
12
13
auto a = {5}; //此时a的类型为std::initializer_list<int>

template<typename T>
void f(T param);

f({11,23,9}); //错误!不能推导出T
1
2
3
4
5
6

# decltype

decltype(expression)就是推导出表达式的类型。直观易理解,其会保留const、volatile等类型,且当expression为右值引用变量时,虽然右值引用变量本身是左值,但是decltype推导出来的仍是右值引用。

const int i=0; //decltype(i)是const int

bool f(const Widget& w);//decltype(f)是bool(const Widget&)

vector<int> v; //decltype(v)是vector<int>, decltype(v[0])是int&

int && a = 5;
auto b1 = 7; //b为int       
decltype(a) b2 = 6; //b为int&&

decltype(auto) b3 = 8;  //b为int
decltype(auto) b4 = std::move(9); //b为int&&  

1
2
3
4
5
6
7
8
9
10
11
12
13

decltype(auto)的使用场景.

template<typename Container,typename Index> //C++ 14版本
auto authAndAccess(Container& c,Index i)
{
    authenticateUser();
    return c[i]; //这里c[i]类型为int&,但是返回值auto会剥离掉&,因此返回的是int
}

std::deque<int> d;
authAndAccess(d,5)=10; //认证⽤⼾,返回d[5],由于返回的是int临时值,无法把10赋值给它, ⽆法通过编译器!
1
2
3
4
5
6
7
8
9

把上面的authAndAccess改成这样就可以了:

template<typename Container,typename Index>
decltype(auto) authAndAccess(Container& c,Index i)
{
    authenticateUser();
    return c[i]; //decltype(auto)表示用decltype的规则推导返回值类型
}
1
2
3
4
5
6

decltype(auto) 可以这样理解:auto说明符表⽰这个类型将会被推导,decltype说明decltype的规则将会应用到这个推导过程中。

编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54
memory order 内存模型
完美转发forward源码分析

← memory order 内存模型 完美转发forward源码分析→

最近更新
01
通过模板实现结构体成员拷贝-按成员名匹配
05-07
02
c++17通过模板获取类成员的个数
05-01
03
avx-sse切换惩罚
04-30
更多文章>
Theme by Vdoing | Copyright © 2019-2023 Colder Leo | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×