你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

深入学习C++函数模板

[复制链接]
gaosmile 发布时间:2020-8-24 22:39
在昨天的文章里面,我们给大家简单的分享了关于c++里面的函数模板的概念,通过代码示例,我们对函数模板的优势有了一个比较清楚的认识。咋们今天继续来深入学习函数模板。以下内容是今天知识学习总结笔记。
一、函数模板的深入理解:
1、函数模板深入理解:
  • 编译器从函数模板通过具体类型产生不同的函数
  • 编译器会对函数模板进行两次编译
    # s, q4 s' U* w* A
-对模板代码本身进行编译,比如检查函数模板是否有语法上的错误
-对参数替换后的代码进行编译;也就是说,我们在调用函数模板的时候,编译器根据实际的参数类型,从而得到真正的函数,这个时候编译器会对这个函数进行第二次编译
2、注意事项:
  • 函数模板本身不允许隐式类型转换
    % v9 g6 E  X2 j: `' e0 z
-自动推导类型时,必须严格匹配
-显示类型指定时,能够进行隐式类型转换
代码实践:
代码版本一:
#include <iostream>
; o' }8 ]; o- n& K0 q5 {6 [( }#include <string>' B+ G6 r3 F- M  h! r1 L) a
0 ~$ m3 f- f# n: ]3 d
using namespace std;
' ?. R% f' l+ c$ f7 l! q0 x8 v  @/ T% f! @" F: B
class Test# f4 c8 L3 D3 e; A1 ]
{
8 Y* |( B9 \9 V     Test(const Test&);; F) i- K" ?7 T2 _5 n
public:
, @' P. a- M$ |     Test()
% \+ I; f& B; ?$ l! O; G1 c' y     {7 O, w3 U+ E4 Y) d9 z+ J7 Y! |

5 a- J" h8 c7 x; _4 y; B7 ^     }' B9 |  ^! Y& h$ y; U
};5 }( z7 M6 j9 ~9 X3 O
1 u2 i2 O- m7 F
template < typename T >& Y0 I& i  U: v4 ~7 D$ u6 t0 Z
void Swap(T& a, T& b)# W- p* |2 N2 g1 t# h4 N- h
{
$ _$ v8 e. [7 g  f: A- O    T c = a;: f0 F* [7 P' |& S7 o
    a = b;
. P. d% s7 {  ~7 A3 ]! e    a = c;
+ Y* l& B8 w' B5 h0 B$ d! Z& R# t}5 Z0 h  d2 s+ x* y
typedef void(fun1)(int&, int&);
5 z- e* g4 s. otypedef void(fun2)(double&, double&);
1 l- R- p- H1 i- S* \' f$ wtypedef void(fun3)(Test&,Test&);
, J; h% D6 B" d- N5 X# l, L
% s( L  P9 Z: a. k7 `/ h! sint main()
% h; |& e: v5 v; q& [; m  U' n* _{3 i0 }) B: j# x$ D/ _, u: U, Z
/* p1 和 p2 指向具体函数,但是 p1 和 p2 指向的具体函数是由函数模板具体产生,这里已经产生了函数,不是模板了,这两个函数是两个独立的不同的函数,我们通过函数打印地址,就能够明显发现 */
. |( `8 ^) h. W& L4 I! [% x    fun1 *p1 = Swap;  // 定义函数指针,并能编译通过;这里编译器通过函数指针的类型自动推导 T 为 int; 编译器默默做的事有:1、首先进行自动推导;2、产生真正的 Swap() 函数;3、将实际生成的 Swap() 函数地址用于初始化 pi;# Y2 A9 @* q: P5 H% o( w; s( }( M) d
    fun2 *p2 = Swap;//编译器自动推导为double
% S1 u9 f- d' ?! c: c, H) B! _& j7 l5 k, s! [, C
    cout<<"p1 ="<<reinterpret_cast<void*>(p1)<<endl;//// 将 p1 这个指针的类型重解释为 void*
1 b, o5 d+ X: K) I0 X# h$ V    cout<<"p2 ="<<reinterpret_cast<void*>(p2)<<endl;
1 b0 R. g5 b' w/ G" [5 K9 ]) r8 s# V9 x9 Z. z8 t. r
   return 0;
, g# u3 e' o1 Y
( x5 Y2 l, p& @8 s5 j, f}
% K, V$ i1 F4 x1 u/ z- e' s! d3 `
; b8 W  ]/ O, T! m6 V5 D( i4 g4 j& s9 c. o8 t; t
输出结果:
root@txp-virtual-machine:/home/txp# ./a.out5 C. y  M. b* M, W1 f- u3 S
p1 =0x4009641 J3 g0 V2 @' t2 q9 P- |) i" I3 \
p2 =0x400990
; g/ q4 J$ Y; u) A, M
0 `1 J# k) A* D1 H1 Z7 P1 Y
代码版本二:
#include <iostream>, W; r4 O& G3 C* X
#include <string>) F1 U0 b) w) f0 T  c& S
. M, u9 v( ~& Y2 S$ `0 {
using namespace std;0 i) |! f/ |, N% c9 C
; {% y: w% `3 D& M; q* t% W
class Test
3 Y9 Z8 F2 i, p2 t6 ?5 b3 T{5 {" r& @0 ^" X/ L; w
     Test(const Test&);. {& l* l4 c# a" f; [* }
public:( I$ E& m0 A4 h* X5 \. c
     Test()
4 i2 p' V0 ]' C6 C3 x8 T" J     {+ ]4 f* m" R1 X

% n! b  J0 Z6 u( j0 @3 S     }$ y1 K& G% [' Q- P. q
};5 o% G3 Q% V4 ^' C6 L! v# ?3 }3 j( N

1 x% t7 c  l+ x4 l8 rtemplate < typename T >; a; ]( X3 I, L2 V$ @! P4 B
void Swap(T& a, T& b)! q& U* o  r% v
{' w6 E/ {: x4 \0 W
    T c = a;
1 e. h) l" m; s. e2 h9 Z    a = b;
2 O9 Z: T, W( T7 U3 V    a = c;
6 Q6 D6 H1 ?$ P/ {* k6 {}' Z# L' Z" `2 d8 c% _  D
typedef void(fun1)(int&, int&);9 r" f/ C( y  G4 O+ J. M5 m; r$ M
typedef void(fun2)(double&, double&);& i  p* G# s+ P6 F; K1 |7 k
typedef void(fun3)(Test&,Test&);
4 w4 _+ Z! t- I2 U8 _
8 y5 `( `+ I/ Z& C( j, bint main()
5 R8 z* a. X6 r2 P{
/ S  U  v, a5 w$ i, _! E% ]; C6 P, E    fun1 *p1 = Swap;( W9 Q* K  m6 `) e2 g
    fun2 *p2 = Swap;. Y: u! d; T- d" z0 u

' i0 m& _, S$ ~/ V$ K7 M    fun3 *p3 = Swap;
6 B& Y/ S) a9 S0 N8 ~. r# Y8 r6 Y" E: G3 U. E. j- `& V
    cout<<"p1 ="<<reinterpret_cast<void*>(p1)<<endl;/ ?: @0 B5 u$ O3 k7 m8 ]
    cout<<"p2 ="<<reinterpret_cast<void*>(p2)<<endl;
( d4 n) T3 Z8 I* \    cout<<"p3 ="<<reinterpret_cast<void*>(p3)<<endl;4 i+ a6 [6 y* `1 [5 n5 z$ z
   return 0;( W. X4 a# `* e4 f

3 Z2 h# d$ f+ a1 Q. i& f}6 e) b3 V' Q4 V6 R
+ ~- K% A+ E' [, F9 X/ ?! ?+ O
输出结果:
root@txp-virtual-machine:/home/txp# g++ test.cpp
1 V1 J, g; t9 ~test.cpp: In instantiation of ‘void Swap(T&, T&) [with T = Test]’:
4 b, D: ^# Q. w8 r; |test.cpp:32:16:   required from here( [' @/ e, R8 t$ [$ H1 O
test.cpp:8:6: error: ‘Test::Test(const Test&)’ is private
/ \7 k* T, c$ |: |% p$ `      Test(const Test&);
& l3 D8 V9 t0 Y0 T5 W7 v( ]      ^
1 c3 P* C) r* d, ~test.cpp:19:11: error: within this context
- `/ B0 O; `2 I6 `3 D1 m/ A     T c = a;% y5 J7 M, Q- s0 J9 f  L# A7 E& @: b
           ^
) q( b& ~9 x6 f' q  z' H* ?
- s% [. z  `$ d3 t0 v2 W
注解:显示编译器自动推导T为Test类,也就是Swap(T&,T&),然后就报了一个错误,说拷贝构造函数是私密的,所以也就导致Test c =a这里报错了
3、多参数函数模板
(1)函数模板可以定义任意多个不同的类型参数
template < typename T1, typename T2, typename T3 >
$ F9 [, i$ F3 ]  mT1 Add(T2 a, T3 b)) k; L$ N1 d1 r- g: n! _8 l
{
8 F8 M  A) A+ r+ Z$ B   return static_cast<T1>(a+b);/ x4 A/ Y2 t2 f( K6 ^8 K4 F
}( j2 u+ ?# w. R$ S
(2)对于多参数函数模板
  • 无法自动推导返回值类型
  • 可以从左向右部分指定类型参数
    * v) R. L; `" L. C
//T1 = int, T2 = double, T3 = double! E- ?/ `3 V, ~
int r1 =Add<int>(0.5,0.8);
! m/ t* D. F4 F; \' z//T1 = int, T2 = float, T3 = double4 b0 G  J0 N) f3 `
int r2 =Add<int,float>(0.5,0.8);
* B9 M6 U0 _& J& c/ ~; E1 `. h//T1 = int, T2 = float, T3 = float6 M# x! u1 n5 l) `
int r3 =Add<int,float,float>(0.5,0.8);, ?$ A, [$ P1 V0 _. K; `) G& X
注:工程中将返回值参数作为第一个类型参数
代码实践:
#include <iostream>5 F7 O7 O; W$ D1 I, ~) q( T
#include <string>* A2 z6 f( I# [
, v7 N! S- |* r
using namespace std;6 r3 V$ o/ w7 r, o, H6 m

$ I. i# m6 v% M9 p. e  ttemplate
3 t  x0 E" r' t: V" A# l# B( q5 `< typename T1, typename T2, typename T3 >1 J$ p$ G4 ^1 `/ j$ v* a2 Z
T1 Add(T2 a, T3 b)
$ q3 r1 g2 C# d{# S* R& x- T, T, ?  i- f
    return static_cast<T1>(a + b);
* ^( X7 C5 `) y* e, ^}
1 `/ Q$ S, L; s4 {# C7 n" f/ u6 u3 M$ K

& l6 f% q1 k5 G6 Y* Z4 U( [int main()- B8 r& ^4 I' C. _2 ^- v
{: V' c0 q1 X  }5 O( h5 o. d' p
    // T1 = int, T2 = double, T3 = double
% T% d$ R, y% m) E1 W* s5 h    int r1 = Add<int>(0.5, 0.8);
% K( }* m) y7 ]+ R
. a% d. [0 m% ]- l    // T1 = double, T2 = float, T3 = double: w0 o3 }8 y  i4 a6 A
    double r2 = Add<double, float>(0.5, 0.8);; G- v2 G  }: ]4 V

$ }  K$ L6 j9 E+ R; s0 b    // T1 = float, T2 = float, T3 = float
" t- ^% v/ M* T! q3 _. h    float r3 = Add<float, float, float>(0.5, 0.8);6 h4 I# F/ G( _# `4 }8 C+ \) x

: q  W3 S4 [$ Z  V1 Q    cout << "r1 = " << r1 << endl;     // r1 = 13 A/ u6 R3 n: c1 g9 i
    cout << "r2 = " << r2 << endl;     // r2 = 1.3
- |1 W8 w7 t7 x; f  m9 \    cout << "r3 = " << r3 << endl;     // r3 = 1.3
) {3 B- _/ M* j3 K    8 K/ k: ^- ^7 {# K3 w% W4 f7 A: q
    return 0;+ b( w: I# T* y& V7 \2 p/ |
}
: a/ h$ I. u# o& C
3 W/ m: r. c% K9 j7 K) F# f; r2 F+ h& S  Q' A: H, M
输出结果:
root@txp-virtual-machine:/home/txp# ./a.out
) @  S" d! ?9 f9 w$ r; t. C" Pr1 = 1) ^+ q6 R2 @/ G- N
r2 = 1.3
0 g7 ]( p/ G  z1 S: Jr3 = 1.3
0 l# e( B1 w& |! S6 ~" P" l9 }( h; a" G6 O% G% A$ k" |  H
4、当函数重载遇到函数模板会发生什么?
(1)函数模板可以像普通函数一样被重载
  • C++编译器优先考虑普通函数
  • 如果函数模板可以产生一个更好的匹配,那么选择模板
  • 可以通过空模板实参列表限定编译器只匹配模板

    & }/ {8 H" G4 j
微信图片_20200824223822.png
代码实践:
#include <iostream>) G4 s! j  Y1 H8 e* e
#include <string>2 v2 k" d/ b2 D
, w% k) s6 ^6 ^& h) X
using namespace std;
! ?- j" b0 k. g4 F# ~
$ x3 x5 c/ Y, t2 O5 K
. m. ]4 d& y/ H5 m( J$ s$ Ytemplate < typename T >' T3 r3 G& s0 o. E6 ^: j) s
T Max(T a, T b)
  {7 Y7 b- t8 H5 z{
$ c- l; d' ]( |1 p) }" m    cout << "T Max(T a, T b)" << endl;
/ m; ?/ i: ^. }; u! S6 \9 F# l    $ g8 n- `! M; D! D" R
    return a > b ? a : b;
8 O  x' M9 ~8 `# {7 H% Z$ N1 w}& h+ ]! U- T, B4 O* `4 c

2 Y# P+ w7 p0 R  h8 L; K) eint Max(int a, int b)1 _# f7 l: g8 H; X; m, q
{; o+ J) G" r) f0 J4 A7 n
    cout << "int Max(int a, int b)" << endl;6 o: w6 }/ g$ r9 |4 a
    + i6 F; L9 L2 g. T! B- F7 E
    return a > b ? a : b;
5 \+ h2 ]& B$ j) i6 [) G3 u- N}. u) N  S5 e" K  \! q

: d/ h. q8 X5 ]- _8 `# x, ytemplate < typename T >
. \7 s" N7 @3 K3 Y: Y4 F4 C* F3 E- a! OT Max(T a, T b, T c)& }# x% P4 W( S! \" I2 h( f
{
  j( `) {2 ]3 j    cout << "T Max(T a, T b, T c)" << endl;
% p) c, W, N* b2 M: N6 I/ {    6 i% w8 `; d9 @6 G$ }8 j0 m" j
    return Max(Max(a, b), c);
5 O  o9 h, _0 |8 J  v7 q}
& F2 O5 M7 `4 I0 _/ x) A
5 m" E) N" P+ X/ y' J7 Jint main(); m2 F; i, G3 u9 i, |
{
. n. a9 b/ k( S9 o: N4 G    int a = 1;
9 U' b7 W# b1 z: E+ }6 |2 k' i    int b = 2;0 r" R6 Z% L: x2 L  Y1 S- u
    7 C1 i6 w* ?- u# J  }5 ^- ^& G
    cout << Max(a, b) << endl;                   // 普通函数 Max(int, int)
! B8 j3 ^6 i9 a    % Q6 K# }( E/ }! O* @# g. Z6 R
    cout << Max<>(a, b) << endl;                 // 函数模板 Max<int>(int, int)
1 E1 s* W6 c/ ~      `( e; K% Y2 K" M
    cout << Max(3.0, 4.0) << endl;               // 函数模板 Max<double>(double, double)5 l" @' N) B& j4 p
   
- ]! z' v4 S9 s  c2 `- v    cout << Max(5.0, 6.0, 7.0) << endl;          // 函数模板 Max<double>(double, double, double)+ q, C/ Y9 ?; f6 s5 q
   
2 o3 Q; V+ U& V2 @    cout << Max('a', 100) << endl;               // 普通函数 Max(int, int)! F' z9 ?/ `& P1 U8 m, @
    0 k: y% X: z# o* y8 p0 h) \5 u- X
    return 0;
; p# @) [7 V, w! p2 C0 G}# [$ r6 V7 G. a% f( N

, z) u5 i* R0 v/ o1 }' e7 [* L$ [4 M" r( S9 G
输出结果:
root@txp-virtual-machine:/home/txp# ./a.out) ^& H) P7 i- h, m
int Max(int a, int b)
8 W6 x$ H& Z( P" {5 k0 T2
* H, T" P! a& W' b3 y  p( sT Max(T a, T b)0 W/ b& z6 x! B$ E" l$ [# A+ e
2
/ b) ?3 I: E7 J5 y8 y+ I: x% k& CT Max(T a, T b)) U0 f5 L6 Q  {! ^% G: `
42 z2 A5 y8 w/ k
I Max(T a, T b, T c)- O+ O/ c0 p" d% h) Y$ \" n
T Max(T a, T b)
: A; G1 ^/ e% j3 Q( lT Max(T a, T b)
3 @9 C) S/ L2 K& K, B7
5 u# X# k$ R7 P+ U$ n# Hint Max(int a, int b)& e" Y2 y# g8 i. i
1007 h$ ~- z/ z/ B. a9 e0 u$ Y- u
' a3 N( o3 |5 S1 N% \3 l6 f
二、总结:
  • 函数模板通过具体类型产生不同的函数
  • 函数模板可以定义任意多个不同的类型参数
  • 函数模板中的返回值类型必须显示指定
  • 函数模板可以像普通函数一样被重载
    5 b4 K7 q2 T# k* M0 y( r. H5 N4 t
好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。

1 \3 u  z; s1 ~4 c, j% e* i1 L
赞 收藏 评论1 发布时间:2020-8-24 22:39

举报

1个回答
davidwyq 回答时间:2020-8-25 20:13:10
学习了
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版