首页 > 其他分享 >模板型别推导

模板型别推导

时间:2022-10-02 00:55:35浏览次数:51  
标签:const 推导 型别 param 引用 func 模板

模板型别推导

下面代码表示:函数模板和函数调用(从 expr 来推导 T 和 ParamType 的型别)。

template<typename T>
void func(ParamType param);  // 函数模板的声明

func(expr);  // expr:表达式,从 expr 来推导 T 和 ParamType 的型别


        在编译期,编译器会通过  表达式 expr  推导两个型别(类型),一个是 T 的型别,一个是 param 的型别,而且这两个型别通常不一样。因为 ParamType 中常常会包含一些饰词,如 const 或引用符号等限定词,例如 ParamType 可以是 const T&

        本质上, T 的型别推导结果,不仅仅依赖于 expr 的型别,还依赖 ParamType 的形式,具体情况分为三种:

  1. ParamType 具有指针或引用型别,但不是万能引用;
  2. ParamType 是一个万能引用;
  3. ParamType 既非指针,也非引用;

       

情况1:ParamType 具有指针或引用型别,但不是万能引用;

在这种情况下,编译器会这样进行类型推导:

  1.  expr 具有引用型别,先将引用部分忽略。
  2. 然后,对 expr 的型别和 ParamType 的型别执行模式匹配,来决定的 T  型别.

举例如下:

template<typename T>
void func(T& param)
{
    T t = 0;
    cout << type_name<decltype(t)>() << endl;      // 这里通过调用自定义的 type_name 函数来获取表达式(T 和 param)的型别
    cout << type_name<decltype(param)>() << endl;
}

int main()
{
    int x = 27;
    const int cx = x;
    const int& rx = x;

    func(x);    // T 的型别是 int,param 的型别是 int&
func(cx); // T 的型别是 const int,param 的型别是 const int&
func(rx); // T 的型别是 const int,param 的型别是 const int& }

  当人们向引用型别的形参传入 const 对象时,他们期望对象保持其不可修改的属性,也就是说,期望该形参成为 const 的引用型别。即该对象的常量性(constness)会成为 T 的类型推导结果的组成部分。

  在第三个调用中,即使 rx 具有引用型别,T 也并未被推导成一个引用。因为 rx 的引用性(reference-ness)会在型别推导过程中被忽略。

  上述规则对于右值引用仍然成立!

 

如果将函数模板的形参类别从 T& 改为 const T&,结果会有一些变化,但仍然满足规则,代码如下:

template<typename T>
void func(const T& param)
{
    T t = 0;
    cout << type_name<decltype(t)>() << endl;      // 这里通过调用自定义的 type_name 函数来获取表达式(T 和 param)的型别
    cout << type_name<decltype(param)>() << endl;
}

int main()
{
    int x = 27;
    const int cx = x;
    const int& rx = x;

    func(x);    // T 的型别是 int,param 的型别是 const int&
    func(cx);   // T 的型别是 int,param 的型别是 const int&
    func(rx);   // T 的型别是 int,param 的型别是 const int&
}

  一如前例,rx 的引用性在型别推导过程中会被忽略,并且函数模板的参数 param 本身具有 const 引用型别,T 的型别推导结果中包含 const 也就没有必要了。 

情况2:ParamType 是万能引用;

当函数模板中的参数是万能引用(即在函数模板中持有型别参数 T 时,万能引用的声明有且只能为 T&&)时,当传入的实参为左值或右值时,表现有所不同。具体如下:

  • 如果 expr 是个左值,T 和 ParamType 都会被推导为左值引用。有两点特别之处:1. 这是在模板型别推导中,被推导为引用型别的唯一情形;2. 函数模板形参是用右值引用语法声明,而它的型别推导结果却是左值引用。
  • 如果 expr 是个右值,推导结果如情况1。
template<typename T>
void func(T&& param)
{
    int m = 0;
   T t = m; cout << type_name<decltype(t)>() << endl; // 这里通过调用自定义的 type_name 函数来获取表达式(T 和 param)的型别 cout << type_name<decltype(param)>() << endl; } int main() { int x = 27; const int cx = x; const int& rx = x; func(x); // T 的型别是 int&,param 的型别是 const int& func(cx); // T 的型别是 const int&,param 的型别是 const int& func(rx); // T 的型别是 const int&,param 的型别是 const int&
func(27); // T 的型别是 int,param 的型别是 int&& }

 

情况3:ParamType 既非指针也非引用;

当 ParamType 既非指针也非引用,此时就是值传递。无论传入的是什么, ParamType 都会是它的一个副本。由 expr 推导出 T 的型别规则如下:

一如之前,若 expr 具有引用型别,则忽略;

忽略 expr 的引用型别后,若是个 const / volatile 对象,也忽略 const / volatile 属性。

template<typename T>
void func(T param)
{
    T t = 0;
    cout << type_name<decltype(t)>() << endl;      // 这里通过调用自定义的 type_name 函数来获取表达式(T 和 param)的型别
    cout << type_name<decltype(param)>() << endl;
}

int main()
{
    int x = 27;
    const int cx = x;
    const int& rx = x;

    func(x);    // T 的型别是 int,param 的型别是 int
    func(cx);   // T 的型别是 int,param 的型别是 int
    func(rx);   // T 的型别是 int,param 的型别是 int
}

  有上述代码可知:若函数模板参数的型别为 const T param,则 T 的型别仍是 int,param 的型别是 const int。

     对于函数实参为既有顶层 const 也有底层 const的对象,那么按值传递会忽略对象的顶层 const 属性,而保留底层 const 属性。如下述代码所示:

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

const char* const ptr = "czw study";

func(ptr);   // T 的型别是 const char*,param 的型别是 const char*

 

数组实参

虽然数组有时会退化成首元素的指针,但当函数模板的参数是引用型别和非引用型别,有很大的区别。

  • 当函数模板的参数不是引用型别时,数组会退化成指针;
  • 当函数模板的参数是引用型别时,数组不会退化成指针;
template<typename T>
void func(T param);

const char name[] = "czw study";

func(name);   // T 的型别是 const char*,param 的型别是 const char*




template<typename T>
void func(T& param);

const char name[] = "czw study";   // name 的型别是 const [10] const

func(ptr);   // T 的型别是 const char [10] const,param 的型别是 const char [10] const&

 因此,可以利用数组引用这一能力创造出一个模板,用来推导出数组含有元素的个数,如下:

// 该函数模板能在编译期获取数组的个数
template<typename T, std::size_t N>
constexpr std::size_t arraySize(T (&)[N]) noexcept   // 模板参数为数组引用: T (&)[N] 
{
    return N;
}

int main()
{
    int keyVals[] = { 1, 3, 7, 9, 11 };   // 该数组的个数在初始化列表中计算得到
    int mappedVals[arraySize(keyVals)];   // 编译成功,说明通过 arraySize 能够在编译期得到数组大小
}

  将 arraySize 声明为 noexcept,是为了帮助编译器生成更好的目标码。

函数实参

除了数组型别退化为指针外,函数型别也会退化为函数指针,并且相关规则与数组型别推导一致。

当函数模板参数按值传递,如下:

void someFunc(int, double)
{
    cout << "123";
}

template<typename T>
void func(T param)   // param 按值传递
{
    cout << type_name<decltype(param)>() << endl;
}

int main()
{
    func(someFunc);   // param 被推导为函数指针,具体型别是: void (*)(int, double)
}

当函数模板参数按引用传递,如下:

void someFunc(int, double)
{
    cout << "123";
}
template<typename T>
void func(T& param)   // param 按引用传递
{
    cout << type_name<decltype(param)>() << endl;
}

int main()
{
    func(someFunc);  // param 被推导为函数引用,具体型别是: void (&)(int, double)
}

  

标签:const,推导,型别,param,引用,func,模板
From: https://www.cnblogs.com/czw-yibao/p/16747154.html

相关文章

  • 误差理论与测量平差基础——例题补充推导
    本次补充推导的是第三章的第六道例题,主要做出的补充推导内容是几个方差计算公式的内容,下面是补充内容  ......
  • 接口中的静态方法——模板方法设计模式
    先看一个简单的例子,相信看完下例,你会对模板方法有一个初步的了解interfaceOperations{voidexecute();//接口中的静态方法staticvoidshow(String......
  • security + cloud模板
    前言​​案例地址​​​​镜像地址​​部署当前项目为cloud+security案例模板,要部署cloud项目,将每个模块打成jar包上传到服务器,之后打成镜像打成镜像后启动容器报错:​​no......
  • mybatis plus 项目模板
    前言​​案例地址​​项目搭建新建1个springboot项目,导入所需依赖点击查看详情<dependencies><dependency><groupId>org.springframework.boot</groupId>......
  • 公共操作与推导式
    一、公共操作(一)运算符运算符描述支持的容器类型+合并字符串,列表,元组*复制字符串,列表,元组in是否存在字符串,列表,元组,字典notin是否不存在字符串......
  • 模板变量替换(正则表达式)
    publicclassTemplateReplaceTest{publicstaticvoidmain(String[]args){Stringsql="INSERTINTO${tableName}"+"(${COLUMNS})......
  • 代码模板存档
    代码模板存档)2022.9.30增加并查集、埃氏筛、线性筛、快速幂、扩展欧几里得、求逆元一般C++比赛文件模板#include<bits/stdc++.h>usingi64=longlong;intm......
  • 模板引擎
    模板引擎的原理正则与字符串操作exec()函数用于检索字符串中的正则表达式的匹配RegExpObject.exec(string)分组正则表达式提取自己想要的内容{{name}}/{{......
  • 模板:函数模板
    1模板的基础知识1.1模板的参数在技术文献中:parameter翻译成形参(formatparameter),argument翻译成实参(actualparameter).Atypeparametercanbeusedtoname......
  • 【模板】BM字符串匹配
    据说效率是KMP的\(3\sim4\)倍。主要利用坏字符和好后缀进行跳转来避免过多的匹配。这篇博客讲的很好,推荐大家看看。#include<iostream>#include<vector>#incl......