friend是C++中封装的漏网之鱼。

C++中的friend同意其它的类或者是函数訪问本类的不论什么成员。甚至是private成员,仅仅要该类声明其为友元。

但是,在有些情况下,并非同意外界訪问类的内部实现而使用友元

这就是在 “模板定义” 与 “隐式类型转换” 之间的一个trick了。

首先,看一个简单的有理数的模板类。该类定义了有理数,而且实现有理数的乘法。

注:下述代码中,将operator*声明为非成员函数。是由于

“假设你须要对成员函数全部的參数(全部的,当然也就包含this指针啦)进行类型转换。那么将该函数声明为非成员函数”。

也就是说,仅仅有当參数位于參数列的时候,这个參数才是隐式类型装换的合格參与者。



updated:这里的參数列指的就是函数的形參列表!!!

可是类的成员函数的那个隐式參数(即this指针)不是隐式转换的合格參与者!。。

这对C++模板类相同适用。


在上述代码中。重载了 * 符号。用于计算两个有理数之间的乘法。

一个自然而然的道理,假设我们须要支持 有理数 * 自然数 ,这是一个无可厚非的要求。当我们才有以下的调用

Rational<int> a = Rational(1,2);

Rational<int> ret = a * 2;

在非模板类中。该函数会将2进行隐式类型转换为Rational对象。再进行乘积运算。

糟糕!编译不通过。在非模板类中这是非常正常的事,但是在模板类中却出现了问题.

简言之。编译器陷入了“纠结”的境界!!

。。

以下一一进行分析:

当编译器看到operator*的调用的时候。编译器不知道我们想要调用什么函数,由于编译器看到这个模板函数时。第一要做的就是将函数实例化出来,也就是要首先判断出T的类型可是几经周折。发现不行。

首先。为了推导出T, 编译器对 * 调用的两个參数进行入手,分别为 Rational<int>   和 int, 由第一个參数能够非常easy的知道得到
T 为int。 可是在第二个实參呢,编译器怎么判断 T 的类型?? 你或许会说,此时编译器就应该使用 Rational<int> 的隐式构造函数 啊。

不就能够 推导出 T 的类型了吗。

可是。编译器绝不会这么做,由于在 模板实參 的推导过程其中是不会 考虑隐式转换的

这是本文最重要的一句话。

因此,在面对这种实參推导 的问题是,friend 便出场了,因为
friend 能够在  模板类 中指明某个特定的函数
。也就是说。在函数调用之前。声明该函数。那么在函数调用时,对应的类模板
就不再须要 依赖于 模板实參的推导了,而仅仅须要对这个友元函数进行參数推导就可以。



因此将operator * 声明为该类的友元之后。编译器的行为便不一样了。

类模板并不依赖于实參的推导(此时operator*函数仅仅是该类的一个模板友元函数)。由于此时的实參推导仅仅施行于该友元模板函数身上,所以编译器总是可以在Rational类实例化的时候得知T。

最最核心的一段话:

在onehalf 对象被定义的时候,Rational函数就被实例化了,对应的,它的友元函数  operator* 也就被实例化出来了,也就是说,此时的operator
*
 不再是一个模板函数了,那么 onehalf  *  2 的时候,便是调用这个已经被实例化的函数了。于是乎。隐式类型转换便能够使用与參数推导了。

可是。另一个问题,便是链接时的问题了,此时,编译器知道我们要调用的是哪个函数了,可是如今那个函数仅仅是被声明在Rational类中,并没有实现它。 假设我们在Rational外部定义该函数,这是行不通的。

因此,仅仅在类定义体中声明该函数,假设不定义的话,连接器便会发出抱怨,找不到定义体。

(updated:这里该函数的定义必须由类定义负责,否则该函数就必须是如今类的外面,那么自然而然该函数就必须得是模板函数,那么參数推导又不起作用了。!!

因此必须定义在函数的内部。!!)

因此,将 operator* 声明为友元函数而且将实现定义在类中。

于是,正确的Rational模板类的定义为:

执行结果为:

大功告成!!

上述代码成功的实现了我们的功能!。!

于是,在类模板定义时。出现了相同的话,假设  在编写一个类模板的时候,而 该类的与模板相关的函数  须要支持函数參数

隐式转换的时候,将该函数定义为模板类类中的friend函数。

updated:模板函数对于參数的类型推导是绝对不会考虑 “构造函数的隐式类型转换的”!

这与一般的函数调用是不一样的。因此我们假设须要对函数的參数进行类型推导,那么就须要将该函数定义为非模板类型,这是编译器就会陷入两难的境界:

然而。对于一般的函数(非模板函数)。编译器是会进行參数推导的(包含调用non-explicit构造函数)!

。!

1在參数推导时使用隐式转换  2 为了让这个函数被具现化,我们又须要将它声明在模板类的内部!

friend!

!!!将函数的声明与定义均置于类内部。

friend keyword 对于模板 并不只不过友元!!!的更多相关文章

  1. C++模板类中友元函数的写法

    首先,已声明好的类Triangle file://Triangle.h template<class T> class Triangle{ public: Triangle(T width ...

  2. c/c++ 模板与STL小例子系列<二> 模板类与友元函数

    c/c++ 模板与STL小例子系列 模板类与友元函数 比如某个类是个模板类D,有个需求是需要重载D的operator<<函数,这时就需要用到友元. 实现这样的友元需要3个必要步骤 1,在模 ...

  3. C++中模板类使用友元模板函数

    在类模板中可以出现三种友元声明:(1)普通非模板类或函数的友元声明,将友元关系授予明确指定的类或函数.(2)类模板或函数模板的友元声明,授予对友元所有实例的访问权.(3)只授予对类模板或函数模板的特定 ...

  4. gcc的bug? c++模板类中友元函数的訪问权限问题

    原文地址:http://stackoverflow.com/q/23171337/3309790 在c++中,模板类中能够直接定义一个友元函数.该函数拥有訪问该模板类非public成员的权限. 比方: ...

  5. C++模板类内友元(友元函数,友元类)声明的三种情况

    根据<C++ Primer>第三版16.4节的叙述,C++类模板友元分为以下几种情况 1.非模板友元类或友元函数. 书上给了一个例子: class Foo{     void bar(); ...

  6. c++ 模板类的 友元函数

    #pragma once #include <iostream> template <class T> class stack { template <class Ty& ...

  7. C++运算符重载 模板友元 new delete ++ = +=

    今天的重载是基于C++ 类模板的,如果需要非类模板的重载的朋友可以把类模板拿掉,同样可以参考,谢谢. 一.类模板中的友元重载 本人喜好类声明与类成员实现分开写的代码风格,如若您喜欢将类成员函数的实现写 ...

  8. 模板类的约束模板友元函数:template friend functions

    本来这篇博客是不打算写的,内容不是很难,对于我自己来讲,更多的是为了突出细节. 所谓template friend functions,就是使友元函数本身成为模板.基本步骤:1,在类定义的前面声明每个 ...

  9. C++11模板友元语法

    第 1 类: 普通类A的 普通类B 友元(一对一友好关系): 无需前置声明class B,当class B第一次出现在friend声明中时,该名字被隐式地认为可见. class A { friend ...

随机推荐

  1. golang使用pprof检查goroutine泄露

    有一段时间,我们的推送服务socket占用非常不正常,我们自己统计的同一时候在线就10w的用户,可是占用的socket居然达到30w,然后查看goroutine的数量,发现已经60w+. 每一个用户占 ...

  2. linux cent os putty 问题彻底解决办法

    出现乱码的根本原因: linux系统和putty使用的编码格式不一致. 解决办法: 1.首先使用命令查看linux当前使用的是什么编码格式 echo $LANG 返回的结果有如下几种情况:1)zh_C ...

  3. 02将代码开源到github(不会使用github的来看看吧)

    github不多说了,新建一个repository.如图: 这个创建好了之后,我们在eclipse中新建项目WeatherPro,安装githubclient. 安装好了之后,打开git bash,进 ...

  4. Apache Lucene

    1.Lucene  -全文搜索引擎 Apache Lucene 是一个基于Java的全文搜索引擎,利用它能够轻易的为Java软件添�全文搜索引擎的功能. Lucene最重要的工作是替文件的每个字索引, ...

  5. VB.NET<机房收费系统个人重构版>你都学会了什么(之五)

    接着上篇我们说的配置文件,今天我们来说一下接口. 1.UML图 2.三层架构 3.Sqlhelper 4.配置文件 5.接口 6.设计模式 什么是接口呢?我们可以将接口理解为用于沟通的中介的抽象化.可 ...

  6. Delphi Windows API判断文件共享锁定状态(使用OpenFile来判断)

    一.概述 锁是操作系统为实现数据共享而提供的一种安全机制,它使得不同的应用程序,不同的计算机之间可以安全有效地共享和交换数据.要保证安全有效地操作共享数据,必须在相应的操作前判断锁的类型,然后才能确定 ...

  7. 李兴华JavaWeb开发笔记

    李兴华JavaWeb开发笔记 1.Java语法-基础 环境变量-JAVA_HOME, PATH, ClassPath 变量名 作用 举例 JAVA_HOME 指向JDK目录 C:\Program Fi ...

  8. 使用FragmentTabhost取代Tabhost

       如今Fragment使用越来越广了,尽管Fragment寄生在Activity下.可是它的出现对于开发人员来说是一件很幸运的事,使开发的效率更高效了.好了以下就说说 FragmentTabhos ...

  9. WPF-19:分享一个样式(左右滑动选中的checbox)

    首先看下效果. 选中: 不选中 样式: <Style x:Key="CheckStyle" TargetType="{x:Type CheckBox}"& ...

  10. Loser应该知道的6个残酷人生事实(血泪翻译)

    Loser应该知道的6个残酷人生事实(血泪翻译) - Acfun - 天下漫友是一家 Loser应该知道的6个残酷人生事实(血泪翻译)