100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > 【C++】模板类的友元函数

【C++】模板类的友元函数

时间:2019-02-08 01:38:29

相关推荐

【C++】模板类的友元函数

模板类友元函数

模板类的友元函数

参考:/dreamer_lhs/article/details/53580088

区分:友元是否为函数模板

非模板友元约束(bound)模板友元,友元类型取决于模板类被实例化的类型,一个实例化模板函数非约束(unbound)模板友元,友元函数模板类型不同于类模板类型,一个通用型函数模板

非模板友元

友元函数不是一个模板函数

template<typename T>class HasFriend{...public:friend void counts(); // 对于所有的HasFriend对象的**非模板友元**friend void report(HasFriend<T> &); // 给非模板友元绑定参数};void counts(){... }void report(HasFriend<int>& t){// 显示化模板类型,此时就是HasFriend<int>模板类的友元}

friend void counts()可能是HasFriend<int>,HasFriend<double>… 的友元,也就是面向所有HasFriend具体对象的友元

counts访问模板类对象的方式可能有:访问全局对象;使用全局指针访问非全局对象;自己内部创建对象;访问静态数据成员

friend void report(HasFriend<T>& t)其本身并不是模板函数,而是使用了一个模板参数,意味着友元函数的定义必须要显式化模板类型

[缺点]:非模板友元若是想要绑定参数,涉及到某些模板类的具体化,就必须显示化模板类型,这让程序严重缺乏灵活性

约束模板友元

友元函数是一个模板函数的实例化,且实例化的类型与类模板类型相关

// **step1.** 先在类定义的前面声明每个**函数模板**template<typename T>class HasFriend;template<typename T>void b_counts();template<typename T>void b_report(T&); // 函数模板// **step2.** 类中将**模板函数**声明为友元template<typename T>class HasFriend{...public:friend void b_counts<T> ();friend void b_report<> (HasFriend<T>&); // <>指出这里是b_report函数模板显式**实例化**声明// friend void b_report<HasFriend<T> > (HasFriend<T>&) 也可以这样// 等同于 template void b_report<HasFriend<T> > (HasFriend<T> &);// 将会使用b_report(TT&) 模板生成一个HasFriend<T> 类型的实例(函数定义)};// **step3.** 为友元提供模板定义template<typename T>void b_counts(){... }template<typename T>void b_report(HasFriend<T>& t) // 函数模板**具体化**,其内部可以与b_report(T &)的定义不同{cout << t.data; // 这里会有智能提示}/*** 也可以这样写,那么b_report<HasFriend<T> > (HasFriend<T>&)就是从这个函数模板实例化而来* template<typename T>* void b_report(T& t)* {*cout << t.data* }*/

friend void b_report<> (HasFriend<T>& t )声明中的<>指出这是函数模板的实例化。<>可以为空,因为可以从函数参数推断出函数模板的模板参数类型为HasFriend<T>

friend void b_counts<T> ()这里没有参数,因此必须使用函数模板语法<T>来指明实例化。如:b_counts<int>()是对应HasFriend<int>模板类的友元,每种HasFriend<T>模板类都有其自己的友元函数b_counts<T>()

[注]:

(1)这里需要显式指出函数模板具体化,<>写法是模板函数实例化的一种语法,表示使用模板生成一个类型的函数定义;

(2)声明定义三步走(类前声明、类内声明、类外定义,类内声明要显式)

探讨友元函数加<>的意义(涉及函数模板的显示实例化、显示具体化)

如:

1. 使用 HasFriend<int> 创建对象 2. 隐式实例化了 HasFriend<int>类3. HasFriend<int> 类内的所有函数于是都被确定(使用int替换所有的T)4. 因为友元函数是模板函数,在类内以显式实例化声明 friend void b_report<> (HasFriend<T> &)模板类的模板类型确定后,该显式实例化声明的T也就确定变为:friend void b_report<HasFriend<int> >(HasFriend<int> &) (一个实例化的模板函数)7. 编译器看到上述声明后,将使用b_report()模板生成一个HasFriend<int> 类型的实例也就是使用b_report()模板生成一个 HasFriend<int> 类型的函数定义

其实在类内声明的b_report<>(HasFriend<T>&)只是函数模板b_report(T& t)一个实例化,当给b_report 传参的实参类型为HasFriend<int>时,它就实例化为了b_report<HasFriend<int> >(HasFriend<int> &),由函数模板实例化的用法可知,在这里就为其生成了定义

如果不加<>对友元函数模板的显式实例化说明,b_report(HasFriend<int>&)会找不到函数的定义,因为它不是一个模板函数,必须要去匹配b_report(HasFriend<int>&)这个具体函数的定义。因为外面的b_report(T& t)是一个函数模板,没有实例化b_report(HasFriend<int>&)也就是没有它的具体定义。在汇编中可以看到,foo是没有被实例化的。

那为什么b_report(T& t)不可以根据实参来实例化出一个b_report(HasFriend<int>&)呢?因为根据匹配规则优先级,会去匹配更加具体的一个函数,也就是b_report(HasFriend<int>&),并没有匹配b_report(T& t),因为没有找到前者的定义,就报了链接错误。

对友元函数加了<>,就在告诉编译器,将b_report(T& t)实例化为HasFriend<T>类型的模板函数,但感觉还没完全具体化,随着模板类的实例化,类内的友元模板函数也随之确定实例。总之,就让链接器知道,友元函数b_report<>(HasFriend<T>&)是有定义存在的。

非约束模板友元

友元函数是一个模板函数

template<typename T>class HasFriend{public:template<typename C, typename D>friend void show(C &, D & );...private:T data;};template<typename C, typename D>void show(C& c, D& d){cout << c.data << d.data;}

约束模板友元,是在类模板外面声明友元模板。通过在类模板内部声明友元模板,可以创建非约束模板友元,即每个函数模板的具体化都是每个类模板具体化的友元(所有类模板具体化的友元)。对于非约束模板友元,友元模板的类型参数和类模板的类型参数是不同的

如:

int main(){HasFriend<int> i(10);HasFriend<double> d(2.3);show(i, d);}

函数调用show(i, d)与下面具体化匹配:

void show<HasFriend<int> , HasFriend<double> > (HasFriend<int>& c, HasFriend<double>& d)

这也说明了它是所有HasFriend模板类的友元

当然也可以这样写,看着更像是参数为HasFriend模板类的友元函数:

template<typename T>class HasFriend{...template<typename C, typename D>friend void show(HasFriend<C> &, HasFriend<D> & );}template<typename C, typename D>void show(HasFriend<C>& c, HasFriend<D>& d ){cout << c.data << d.data; // 这样有智能提示}

调用show(i, d)时,类型推导的就变成了:

void show<int, double> (HasFriend<int>& c, HasFriend<double>& d)

主要影响的是show<C, D>模板函数的模板参数类型的推导。但是还是不要写成这样,因为这样会使友元被约束住了(参数类型只能是HasFriend<T>的某个具体化,而不是通用的),比如:

// 将show 改为:template<typename C, typename D>show( HasFriend<C>& c, HasFriend<D>& d ){cout << c.data;cout << d;}HasFriend<int> i(10);show(i, 10); // 错误,第二个参数错误

假如并不都需要直接访问两个模板类的成员,就没必要写成这样。将show写为 show(C& c, D& d) 的通用性更强,所以更能体现**”非约束“**一词

💡 约束 的原因:函数模板显式实例化语法<>使一个函数模板实例化,实例化的模板函数的参数是一个模板类型,参数类型受到类模板的实例化类型影响。因此每生成一个具体的类,就会实例化一个与其对应的友元函数。

受模板类的类型影响→约束;不受模板类的类型影响→非约束

[总结]:如果类模板的友元函数是函数模板实例化,就是约束模板友元;如果类模板的友元函数是模板函数,就是非约束模板友元。

[建议]:关于模板友元——约束与非约束:

为了保持“约束”与”非约束“的风格统一性,如果一定会使用到模板类成员,就使用约束写法;如果不一定会使用到模板类成员,就使用非约束写法

// 约束模板友元写法// 类前声明:template<typename T>class HasFriend;template<typename T>void foo(T &);// 类内声明:friend void foo<> (HasFriend<T> &); // 对函数模板实例化// 类外定义:template<typename T>void foo(HasFriend<T>& t) // 函数模板的具体化,函数重载{// 直接访问模板类成员 t.data 还有智能提示,岂不美滋滋}

// 非约束模板友元写法// 类内声明:template<typename C, typename D>friend void foo(C &, D &);// 类外定义:template<typename C, typename D>void foo(C& c, D& d){// 用到c.data ,d是其他类型,不是 HasFriend的具体类}

模板类友元函数总结

根本原理,就是函数模板的具体化:约束条件,就是在类内对函数模板实例化声明;非约束条件,就是在类内声明一个与类模板类型不同的函数模板

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。