C++模板编程(12)---更深入的基础技术:友元(Friend)

Friend 声明语句的基本概念很简单:指定某些classes或functions,让他们可以对friend声明语句所在的class进行特权存取。但是下面两个事实使这个概念复杂化了:

1)friend声明的语句可能是某一entity的唯一声明;

2)friend函数声明可以就是其定义。

friend class 声明语句不能成为一个定义式,这就大大降低了问题的发生。涉及template时,唯一需要考虑的是新增情况:你可以把某个class template的实例声明为friend:

template  <typename T>

class Node;

template <typename T>

class Tree{

        friend class  Node<T>;

        ...

};

在class template的某一实体成为其它class或class template的friend之前,class template Node必须已经被声明可见。但对常规class来说,没有这个限制。

template <typename T>

class Tree{

        friend class Factory; //OK,即使是Factory的首次声明

        friend class Node<T>; // Error(如果没有声明过Node)

}

1.友元函数Friend Function

    Function template的具体实现实例可以成为别人的一个friend function。前提是该function template名称之后必须紧跟着以角括号括起的自变量列或者说实参列表,如果编译器可推导出所有自变量,自变量列表可以为空。

template <typename T1, typename T2>

void combine(T1,T2);

class Mixer

{

        friend void combine<>(int&, int&); //OK, T1=int&, T2= int&

        friend void combine<int, int>(int,int); //OK; T1 = int, T2 = int

        friend void combine<char>(char, int); //OK,T1= char, T2 = int

        friend void combine<char>(char& ,int); //Error, not match

        friend void combine<>(long,long) {...} //Error, 不能在此处定义函数

}

注意,我们无法定义一个template实体instance,最多只能定义一个特化体specailization。因此一个令某实体获得名称的friend声明语句,不能够是个定义式。

如果friend名称之后没有跟着角括号,有两种可能:

1)如果这不是个资格修饰名称(不含::),就绝不会引用某个template实体;

2)如果这是一个资格修饰名称(含有::),它就必定引用一个先前已经定义的function或function template。

以下例子有助于理解各种可能情况:

void multiply(void*) //ordinary function

template <typename T>

void multiply(T);    //function template

class Comrades

{

        friend void multiply(int) {} //定义了一个新函数::multiply(int)

        friend void ::multiply(void*); //引用先前的常规函数

        friend void ::multiply(int);

        friend void ::multiply<double*>(double*); //修饰名称可以带角括号,但编译器必须见到模板

        friend void ::error() {} //Error:带修饰资格的friend,不能是个定义。

}

如果把friend声明于class templates中,先前所说的规则也全部适用,而且template parameter可参与到friend function之内:

template <typename T>

class Node {

        Node<T>* allocate();

...

};

template <typename T>

class List {

        friend Node<T>* Node<T>::allocate();

        ...

};

然而把friend function定义于class template中可能会引发一个有趣的错误因为任何只在template中被声明的object,都是直到template被实例化后才能成为具体实体concrete entity。考虑下面例子:

template <typename T>

class Creator {

        friend void appear() { //定义一个新函数

               ...

        }

};

Creator<void > mirache;  //::appear()此时生成

Creator<double> oops; //Error: 试图再次生成appear()

在这个例子中,两个不同的具体实现产生出两个完全相同的定义,违反ODR原则。

因此,我们必须确保class template的 template parameters出现在定义于该tamplate之内的所有friend functions的类型中。现在对先前的例子做些改动:

template <typename T>

class Creator {

        friend void feed(Creator<T>*) {

        ...

        }

};

Creator<void> one; //产生:::feed(Creator<void>*)

Creator<double> two; //产生:::feed(Creator<double>*)

这个例子中每个Creator都实例化产出一个不同的函数。注意,虽然这些函数是在template实例化过程中产生的,但它们还是常规函数,不是某个模板的实体。

2.友元模板Friend Templates

通常当我们定义一个friend,而它是个function或是个class template时,我们可以明确指定以哪一个物体entity作为friend。但有时候把一个template的所有实体都指定为某个class的friend也相当有用。这是经由一种所谓的friend template机制实现的。例如,

class Manager

{

        template <typename T>

                friend class Task;

        template <typename T>

                friend void Schedule<T>::dispatch(Task<T>*);

        template <typename T>

                friend int ticket() {

                        return ++Manager::counter;

                }

        static int counter;

};

和常规friend声明语句一样,只有当friend template产生一个无修饰函数名(unqulified function name),而且该名称之后不紧跟角括号,这个friend template才可以是一个定义式。

Friend template 只能声明primary template及primary templates的成员。任何与该primary template相关的偏特化和明确特化体都将自动被编译器视为friends。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值