CPP {模板函数,可变长模板函数,函数模板特化}

CPP {模板函数,可变长模板函数,函数模板特化}

模板函数

定义

预编译阶段, 扫描*.cpp里出现的所有实例函数(比如a.cpp有<int>, b.cpp里有<string>), 则会实例化<int>, <string>;
. 当然a.cpp里有<int>, 那么你要保证 a.cpp里 至少要有<class T>声明;

性质

用模板的一个好处, 自己不用包含头文件 由用户去包含, 参见DATA::DB::Select的设计;

@DELI;

#特化模板函数 与 重载函数, 的使用区别#;
对于Get_bitCount( int/int64/...)(参数必须是整型), 这个场景, 用模板呢? 还是用重载呢?
用模板好, 即template<T> int F(); (只声明不实现); template<> int F<int>(int){...} template<> int F<int64>(int64){...};
因为 他们的代码底层/函数参数个数是完全一致的, 就是求二进制1的个数;
如果用重载, 会有如下问题: 如果你不小心修改了某一个函数的名称, 此时不会报错 (而如果是模板函数 他会报错), 这不好 因为他们就是同一个函数逻辑, 名称应该都一样;
. 一般, 如果你的函数参数不同, 此时用重载, 比如Replace( string & _cur, int _l, int _r, string const& _new)Replace( string & _cur, const string & _raw, const string & _new), 函数参数不同, 实际上 代码底层 也是不同的, 一个是把[l-r]替换, 一个是把所有的raw字串给替换;

@DELI;

函数可以缺省模板参数, template<T,V> void F(...) 你写F<T>(...) 他可以根据...参数 来可以推导V;

@DELI;

#变长模板函数: <class ... T>#;
宏的参数 可以是变长, 模板函数也可以是变长的 即参数个数可以任意 (普通函数不能变长, 必须是模板函数);

template< class _T> void D( _T _t){ cout<< _t;}  
template< class _H, class... _T> void D( _H _h, _T... _t){ cout<< _h << ","; D( _t ...);}

@DELI;

错误

template< class _t, std::size_t _siz> struct __IsContainer_Unwrap<std::array<_t,_siz> > : std::true_type{};
如果你写成int _siz 这是错误的, 因为std::array她模板类的定义 就是用的size_t

@DELI;

#模板函数 声明和定义不可以分开, 必须实现要写到hpp里面#

看以下代码

g.h:  template< class _T_> extern void F( _T_);

b.cpp:  
template< class _T_> void F( _T_ a){ DE_(_a;}}
void G(){  F(1);}

a.cpp:
int main(){
	F(1);
}

他是没问题的, 是因为 b.cpp里 你调用了F(1) 他实例化了F<int>; 所以你a.cpp里可以访问F<int>;
. 但是 如果你a.cpp里调用F( 1.2) 这就报错了, 说明你b.cpp里对F()函数的实现 并没有用, a.pp里是用不了的!
因此 模板函数的声明实现 必须放一起;
. 但有个疑问: std::sort他的实现放哪了? (可能因为他是动态库 所以情况不一样?)

@DELI;

#模板函数与普通函数 不要同时使用#

void Cout();
template< class _H, class... _T> void Cout( _H _h, _T... _t){ cout<< _h;  Cout(_t...);}
不要写这种代码, 因为 一个是普通函数 一个是模板函数, 这导致:  模板函数他的实现在HPP文件里, 而普通函数他的声明在HPP 而实现在CPP里面, 这就很别扭 明明是同一个函数 可却分开了;

改成以下形式:
template< class... _Zero_> void Cout( _Zero_... _a){ assert(sizeof...(_a)==0); } // 接受空参;
template< class _One_> void Cout( _One_ _a){ cout<<_a;} // 1个参数
template< class _H, class... _T> void Cout( _H _h, _T... _t){ cout<< _h; Cout(_t...); // >=2 个参数;
这样, 他们都是模板参数, 直接放到头文件里面即可, 不用分开了;

@DELI;

模板函数 他的作用域是当前文件, 即他和静态函数是一样的, 因此 不支持链接;
比如: A.cpp里 你声明了template<T> void F(T);, 然后在B.cpp里 你实现了template<T> void F(T){ ...}; 于是你把他俩链接到一起, 发现 你在A.cpp里面 还是无法调用F(T)函数;
. 因为F(T)函数的实现 他只生效于B.cpp里面; (如果他不是模板函数 而是普通函数, 这没问题, 可是对于模板函数 他不支持链接!);

可变长模板函数

性质

template< int... _a>, 对于常量 也可以; 即调用是<>, <1>, <1,2>, ...;

@DELI:

参数可以为;
template< class... T> void F( T... _t); 执行F()是可以的, 此时sizeof...(_t) == 0;

@DELI;

额外参数 要写在开头, 即template< class _H, class... _T> string F( int a, const _H & _h, const _T & ... _t), int a要在开头, 此时递归终止函数是F(int){};

@DELI;

空参函数(即递归终止函数), 必须要写;
即便你写成以下代码

template< class _H, class... _T> string F( const _H & _h, const _T & ... _t){  
	if( sizeof...( _t) > 0){ F( _t...);}
}

. 你可能认为, 递归终止是(_h, 空) 不需要写一个F()的函数; 这是错误的, 会报错, 因為F的函數定義 是1 + >=0>=1個參數, 可以編譯器看到F( _t...) 這個參數_t... 是表示>=0個參數, 即F(_t...)要匹配一個接受>=0個參數的F函數; 总之 你必须写一个F(){}的普通函数 作为递归终止;
. 因此, 你這個if判斷 可以去掉;

@DELI;

#sizeof...获取参数个数#

template< class... T>
void F( const T &... _arr){
	`sizeof...( _arr)`等于`...`里的参数个数;
}

F( 1, .0, "abc')的参数个数为3;

模板函数的全特化

性质

特化, 他必须完全和模板函数的参数 完全对应;
比如模板是template< T> T* Func( T const&&), 如果你要对T== int*进行特化, 就等价于 让模板中的T全部替换成int*, 即特化是template<> int* * Func< int* >( int* const&&); (修饰符一个都不能差 比如把const丢掉就错了);

@DELI;

特化函数 必须放在模板函数的下面 (否则你特化 不知道是对谁特化呀…)
模板函数可以直接调用其特化函数, 有点像类函数 但不完全一样;

template< class _T> void F(){ F<int>(); F<string>();}
template<> void F<int>(){ DE_(2);}
template<> void F<string>(){ DE_(3); F<int>();}

F()模板函数 可以调用其特化;  但是你在`F<int>`是不能调用`F<string>`的;
但注意 如果你写成再添加template<T,F> void F()`, 他不是F<T>的特化!  他是函数重载;
`.` 因此, 虽然说大多情况下 模板函数不用采用*声明+定义分离*的形式(即模板函数都是放在头文件里面的), 但是模板函数的声明 还是有用处的, 一个函数F 他有若干个模板函数`<T>, <T,V>, ...` 他们之间要相互调用 此时就需要声明; 

@DELI;

函数只能全特化, 不能偏特化;

错误

类的(静态/非静态)函数, 虽然允许是模板函数, 但不允许特化! (只有普通函数允许特化);
他会报错 error: explicit specialization in non-namespace scope 'struct ST';

补救措施是: 使用函数重载 来替换特化, 即把成员函数写成 模板函数 + 非模板函数重载, 如下:

template< class _T> struct __toString<_T, 0>{ // Atom
	template< class _t> static string TOstring( _t const& _v){ ...}
	static string TOstring( unsigned __int128 const& _v){ ...} // 不能写成特化
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值