模板类友元函数

模板类的友元函数

参考:https://blog.csdn.net/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) 的通用性更强,所以更能体现**”非约束“**一词


【C++】模板类的友元函数相关推荐

  1. 学习笔记-----C++模板类中友元函数重载输出运算符时提示无法解析的外部符号解决方案

    今天在写单向链表实现线性表的时候打算重载输出运算符用来将线性表输出,结果无奈又遇到了问题. 大致代码如下 <pre name="code" class="cpp&q ...

  2. C++中模板类使用友元模板函数 和友员非模版函数!使用不当出现编译错误:无法解析的外部符号…

    在c++海大本科课程设计的最后章节(模板使用 )中涉及到了在Mat类模板中定义友元函数friend Mat<T> operator+(Mat<T> &m, T num) ...

  3. C++知识点61——typename与class、模板编程与继承、模板类和友元、类模板与static成员

    一.typename与class的异同 1.啥时候既可以使用typename,又可以使用class? 当表示模板参数的时候,二者没有区别 2.啥时候只能使用typename,不能使用class? 当模 ...

  4. “模板类与友元”那些事(C++)

    2019独角兽企业重金招聘Python工程师标准>>> 模版类就是类模版实例化之后的类,友元就是一种对函数访问权限的控制,通过将函数设为友元函数让其能够访问其他外部函数不能访问的&q ...

  5. C++ 模板类和友元

    模板类的友元分三类: 1,非模板友元. 2,约束模板友元,即友元的类型取决于类被实例化时的类型. 3,非约束模板友元,即友元的所有具体化都是类的每一个具体化的友元. 1,模板类的非模板友元函数 cou ...

  6. 运算符重载为类的友元函数

    运算符重载为类的友元函数 友元函数通过类的对象可以访问类的公有.保护和私有成员,也就是类的所有成员友元函数都能访问到.所以运算符重载为类的友元函数以后也可以访问类的所有成员. 与运算符重载为成员函数时 ...

  7. 【C++ 基础篇:21】:friend 友元四连问:什么是友元?友元类?友元函数?什么时候用友元?

    本系列 C++ 相关文章 仅为笔者学习笔记记录,用自己的理解记录学习!C++ 学习系列将分为三个阶段:基础篇.STL 篇.高阶数据结构与算法篇,相关重点内容如下: 基础篇:类与对象(涉及C++的三大特 ...

  8. C++总结 友元类和友元函数

    在网上看到的文章, 写的挺全的,  具有总结性质. 于是转过来了.. 源地址(呵呵, 他也是转的)http://www.cnblogs.com/skyczw/archive/2008/05/27/12 ...

  9. 当类模板遇到了友元函数

    在我学习数据结构的时候,选用了<数据结构(用面向对象方法与C++语言描述)>殷人昆 编著 这本教材.这本书代码较老有一些错误,好多是无法直接在新版本编译器下实现的. 当时我C++模板学的不 ...

最新文章

  1. 生成树、最小生成树的一些性质以及邻域的概念
  2. 润乾集算报表提升性能之可控缓存
  3. 有关Nodejs的一些插件介绍
  4. python xlsxwriter 在 flask 中的使用
  5. 计算机应用基础 试列出windows中运行一个程序的三种途径,2012-10-17——郑州大学远程教育学院2012计算机应用基础考试考题和答案.doc...
  6. java 雅思_基于JAVA的雅思考试管理系统的设计与实现(SSH,MySQL)(含录像)
  7. 从numpy里加载_PyTorch强化:01.PyTorch 数据加载和处理
  8. dateutils 工具类_五金工具泡壳封边机
  9. 树莓派、debian 下安装 phantomjs casperjs
  10. RFC1323 timestamp PAWS的实现陷阱
  11. displaytag.properties
  12. 老毛桃发帖子 去广告
  13. 2019牛客多校第8场
  14. CFS三层内网环境 渗透流程
  15. 几个鲜为人知但很有用的 HTML 属性
  16. 锐目对讲机的使用方法详解
  17. svn 423 Locked
  18. windows系统卸载VMware Workstation 并删除注册表残留信息
  19. 自动与Internet时间服务器同步
  20. HOG 特征的提取--基于scikit-image

热门文章

  1. 你有用过Type-C接口的手电筒吗?
  2. Jest enzyme 进行react单元测试
  3. 友盟统计使用及添加测试设备(设备ID及Mac地址识别)
  4. scikit-learn机器学习八 (数据预处理)
  5. ED1 SoC Linux环境搭建
  6. 科研狗citavi/mendeley+word使用技巧外加读论文瞎琢磨
  7. AP计算机科学笔记,AP想5分,不看过来人的笔记怎么行?
  8. 关于产品的一些思考——豌豆荚之应用下载
  9. 吴莫愁公布恋情爱上哈林 演唱会庾澄庆单膝跪地似求婚
  10. 微信修改基本配置信息