栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Effective Modern C++[实践]->理解模板类别的推导

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Effective Modern C++[实践]->理解模板类别的推导

理解模板类别的推导
  • 绪
  • ParamType 是个指针或引用,但不是万能引用
    • 情形1 `f(T& a)`
      • 方式1
      • 方式2
      • 结论如下:
    • 情形2 f(const T& a)
      • 代码
      • 结论
    • 情形3 `f(T* a)`
      • 代码
      • 结论
      • 情形4 f(const T* a)
  • ParamType 是个万能引用
  • ParamType 既非指针也非引用
  • 数组实参
  • 函数实参

绪

函数模板伪代码如下:

template
void f(ParamType param);

一次调用的形如f(expr);,编译器用 expr 来推导两个类型: T 和 ParamType。 这些类型经常是不一样的,因为 ParamType 一般带有限定符,如 const 或引用。
T 的类型推导不仅仅依赖于expr的类型, 还依赖于 ParamType的格式。具体需要从三个方面讨论:
如何查看模板类型参数导出的类型的方法
方式1:
见如下连接
How can I see the type deduced for a template type parameter?
方式2:
点击网址打开Compiler查看生成的代码,如下

ParamType 是个指针或引用,但不是万能引用

类型推导步骤为:

  1. 若expr具有引用类型,先将引用部分忽略
  2. 之后,对expr的类别和ParamType的类别进行匹配来决定T的类别。
情形1 f(T& a) 方式1

使用删除的模板函数

#include 
#include 
#include 

using namespace std;
//情形1
template
void f(T& a)=delete ;
int main() {
    int x = 22;
    const int lx = x;
    const int &rx = x;
    int&& rx1 = 27;
    const int*px = &x;
    int*px1 = &x;

    f(x); 		
    f(lx);	
    f(rx);		
    f(rx1);       
    f(px);    
    f(px1);   
    f(*px);     
    f(*px1);    
}

编译输出如下:

: In function 'int main()':
:17:6: error: use of deleted function 'void f(T&) [with T = int]'
   17 |     f(x);
      |     ~^~~
:8:6: note: declared here
    8 | void f(T& a)=delete ;
      |      ^
:18:6: error: use of deleted function 'void f(T&) [with T = const int]'
   18 |     f(lx);
      |     ~^~~~
:8:6: note: declared here
    8 | void f(T& a)=delete ;
      |      ^
:19:6: error: use of deleted function 'void f(T&) [with T = const int]'
   19 |     f(rx);
      |     ~^~~~
:8:6: note: declared here
    8 | void f(T& a)=delete ;
      |      ^
:20:6: error: use of deleted function 'void f(T&) [with T = int]'
   20 |     f(rx1);
      |     ~^~~~~
:8:6: note: declared here
    8 | void f(T& a)=delete ;
      |      ^
:21:6: error: use of deleted function 'void f(T&) [with T = const int*]'
   21 |     f(px);
      |     ~^~~~
:8:6: note: declared here
    8 | void f(T& a)=delete ;
      |      ^
:22:6: error: use of deleted function 'void f(T&) [with T = int*]'
   22 |     f(px1);
      |     ~^~~~~
:8:6: note: declared here
    8 | void f(T& a)=delete ;
      |      ^
:23:6: error: use of deleted function 'void f(T&) [with T = const int]'
   23 |     f(*px);
      |     ~^~~~~
:8:6: note: declared here
    8 | void f(T& a)=delete ;
      |      ^
:24:6: error: use of deleted function 'void f(T&) [with T = int]'
   24 |     f(*px1);
      |     ~^~~~~~
:8:6: note: declared here
    8 | void f(T& a)=delete ;
      |      ^

从输出的结果可看出推导的类型

方式2
// Compile with /W4

#include 
#include 
#include 

using namespace std;

template
void f(T& a){
    cout<<__func__<<"    "<
    int x = 22;
    const int lx = x;
    const int &rx = x;
    int&& rx1 = 27;
    const int*px = &x;
    int*px1 = &x;

    //void f(int&)
    f(x); 		//T  int;		ParamType  int&
    //void f(int const&)
    f(lx);		//T const int; ParamType  const int&
    //void f(int const&)
    f(rx);		//T const int; ParamType  const int&
    // void f(int&)
    f(rx1);             //T  int;		ParamType  int&
    //void f(int const*&)
    f(px);      //T  const int*;	ParamType const int *&
    //void f(int*&)
    f(px1);     //T   int*;		ParamType  int*&
    //void f(int const&)
    f(*px);     //T const int;     ParamType  const int&
    //void f(int&)
    f(*px1);    //T  int;		    ParamType  int&
}

生成的汇编代码如下:

...
        mov     rdi, rax
        call    void f(int&)
        lea     rax, [rbp-24]
        mov     rdi, rax
        call    void f(int const&)
        mov     rax, QWORD PTR [rbp-8]
        mov     rdi, rax
        call    void f(int const&)
        mov     rax, QWORD PTR [rbp-16]
        mov     rdi, rax
        call    void f(int&)
        lea     rax, [rbp-40]
        mov     rdi, rax
        call    void f(int const*&)
        lea     rax, [rbp-48]
        mov     rdi, rax
        call    void f(int*&)
        mov     rax, QWORD PTR [rbp-40]
        mov     rdi, rax
        call    void f(int const&)
        mov     rax, QWORD PTR [rbp-48]
        mov     rdi, rax
        call    void f(int&)
        mov     eax, 0
        leave
        ret
 ...

输出结果为

f    22
f    22
f    22
f    27
f    0x7ffdfd4ae16c
f    0x7ffdfd4ae16c
f    22
f    22
结论如下:
expr f(expr)TParamTypeinstance
int xf(x)intint&void f(int&)
const int lx f(lx)const intconst int&void f(int const&)
const int &rxf(rx)const intconst int&void f(int const&)
int&& rx1f(rx1)intint&void f(int&)
const int*pxf(px)const int*const int* &void f(int const*&)
int*px1f(px1)int *int* &void f(int*&)
const int*pxf(*px)const intconst int&void f(int const&)
int*px1f(*px1)intint&void f(int&)

情形2 f(const T& a) 代码
// Compile with /W4

#include 
#include 
#include 

using namespace std;

template
void f(const T& a){
    cout<<__func__<<"    "<
    int x = 22;
    const int lx = x;
    const int &rx = x;
    int&& rx1 = 27;
    const int*px = &x;
    int*px1 = &x;

    f(x); 		
    f(lx);	
    f(rx);	
    f(rx1);       
    f(px);   
    f(px1);   
    f(*px);    
    f(*px1);   
}

生成汇编代码如下:

...
        call    void f(int const&)
...
        call    void f(int const&)
...
        call    void f(int const&)
...
        call    void f(int const&)
...
        call    void f(int const* const&)
...
        call    void f(int* const&)
...
        call    void f(int const&)
..
        call    void f(int const&)
结论
expr f(expr)TParamTypeinstance
int xf(x)intconst int&void f(int const&)
const int lx f(lx)intconst int&void f(int const&)
const int &rxf(rx)intconst int&void f(int const&)
int&& rx1f(rx1)intconst int&void f(int const &)
const int*pxf(px)const int*const int* &void f(int const* const&)
int*px1f(px1)int *int* &void f(int* const &)
const int*pxf(*px)intconst int&void f(int const&)
int*px1f(*px1)intconst int&void f(int const&)

情形3 f(T* a) 代码
// Compile with /W4

#include 
#include 
#include 

using namespace std;

template
void f(T* a){
    cout<<__func__<<"    "<<*a<
// void f(T*& a)=delete;

int main() {
    int x = 22;
    const int lx = x;
    const int &rx = x;
    int&& rx1 = 27;
    const int*px = &x;
    int*px1 = &x;

    f(&x); 		
    f(&lx);	
    f(&rx);	
    f(&rx1);       
    f(px);   
    f(px1);   
    // f(*px);  //  no matching function for call to 'f(const int&)'
    // f(*px1); //error: no matching function for call to 'f(int&)'  
}

生成的汇编代码如下:

   		 call    void f(int*)
		 ...
        call    void f(int const*)
		...
        call    void f(int const*)
		...
        call    void f(int*)
		...
        call    void f(int const*)
		...
        call    void f(int*)
		...
结论
expr f(expr)TParamTypeinstance
int xf(x)intint&*void f(int *)
const int lx f(lx)const intconst int*void f(int const*)
const int &rxf(rx)int constconst int*void f(int const*)
int&& rx1f(rx1)intint*void f(int*)
const int*pxf(px)const intconst int*void f(int const*)
int*px1f(px1)int *int*void f(int*)

情形4 f(const T* a)

与情形2类似不做讨论

ParamType 是个万能引用

推导规则会区分左值和右值

  1. expr是左值,T和ParamType都会被推导为左值
  2. expr是右值,则应用《 ParamType 是个指针或引用,但不是万能引用》规则。
// Compile with /W4

#include 
#include 
#include 

using namespace std;

template
void f4(T&& a)=delete ;

int main() {
    int x = 22;
    const int lx = x;
    const int &rx = x;
    const int && yy = 27;

    f4(x); // 左值 :T   int&;	         ParamType int &
    f4(lx);// 左值 :T   const int&;	 ParamType const int&
    f4(rx);// 左值 :T   const int&;	 ParamType const int*
    f4(26);// 右值 :T   int;	 	     ParamType int &&
    f4(yy);// 左值 :T   const int&;	 ParamType const int&
}

编译结果如下

: In function 'int main()':
:18:7: error: use of deleted function 'void f4(T&&) [with T = int&]'
...
:19:7: error: use of deleted function 'void f4(T&&) [with T = const int&]'
...
:20:7: error: use of deleted function 'void f4(T&&) [with T = const int&]'
...
:21:7: error: use of deleted function 'void f4(T&&) [with T = int]'
...
:22:7: error: use of deleted function 'void f4(T&&) [with T = const int&]'
...

详见右值引用的相关博客

ParamType 既非指针也非引用
template
void f(T a)=delete ;

这种情况就是按值进行传递,无论下述代码中,传入什么,a都会是它的一个副本,也就是说是一个全新的对象。

推到规则如下:

  1. 若expr是引用类别,那么忽略掉
  2. 忽略掉expr的引用特性后,若expr是const对象也忽略,若expr是个volatile对象同样忽略。
// Compile with /W4

#include 
#include 
#include 

using namespace std;

template
void f(T a)=delete ;


int main() {
    int x = 22;
    const int lx = x;
    const int &rx = x;
    const int && yy = 27;
    const char*const ptr = "xi_men_chui_xue";

    f(x);
    f(lx);
    f(rx);
    f(26);
    f(yy);
    f(ptr); //T 为const char * 
}
:20:6: error: use of deleted function 'void f(T) [with T = int]'
...
:21:6: error: use of deleted function 'void f(T) [with T = int]'
...
:22:6: error: use of deleted function 'void f(T) [with T = int]'
...
:23:6: error: use of deleted function 'void f(T) [with T = int]'
...
:24:6: error: use of deleted function 'void f(T) [with T = int]'
...
:25:6: error: use of deleted function 'void f(T) [with T = const char*]'
...

注: 在推导过程中,ptr所指内容的常量性会得到保留,但是其自身的常量性被忽略

数组实参
#include 
#include 
#include 

using namespace std;

template
void f(T a){} ;

template
void f1(T& a){} ;

template
constexpr std::size_t arraySize(T (&)[N])noexcept{
    return N;
}

int main() {
    const char name[]= "xi men chui da xue";

    f(name);
    f1(name);
    arraySize(name);
}

生成的汇编代码如下:

...
        call    void f(char const*)
...
        call    void f1(char const (&) [19])
...
        call    unsigned long arraySize(char const (&) [19ul])
...

结论:

  1. ParamType非指针非引用 即按值传递,按值传递给函数模板的数组类别会被推导程指针类别。
  2. 按引用传递给函数模板的会被推导程数组类别,这个类别会包含数组的尺寸
函数实参

同数组实参类似。

#include 
#include 
#include 

using namespace std;

template
void f(T a){} ;

template
void f1(T& a){} ;

void function(){
}

int main() {
    const char name[]= "xi men chui da xue";
    f(function);//退化为指针
    f1(function);//推导为函数引用

}

汇编代码如下:

        mov     QWORD PTR [rbp-32], rax
        mov     QWORD PTR [rbp-24], rdx
        mov     DWORD PTR [rbp-17], 6649208
        mov     edi, OFFSET FLAT:function()
        call    void f(void (*)())
        mov     edi, OFFSET FLAT:function()
        call    void f1(void (&)())
        mov     eax, 0
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/993688.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号