C/C++编程:友元与模板

编程入门 行业动态 更新时间:2024-10-25 14:31:57

C/C++编程:友元与<a href=https://www.elefans.com/category/jswz/34/1770549.html style=模板"/>

C/C++编程:友元与模板

友元声明的基本概念是很简单的:给予某个类或者函数访问友元声明所在的类的权利。然而,由于下面两个事实,这些简单概念却变得有些复杂:

  • 友元声明可能是某个实体的唯一声明
  • 友元函数的声明可以是一个定义

友元类的声明不能是类定义,因此友元类通常都不会出现问题。在引入模板之后,友元类声明的唯一变化是:可以声明一个特定的类模板实例为友元

template<typename T>
class Node;template<typename T>
class Tree{friend class Node<T>;
};

显然,如果要把类模板的实例声明为其他类(或者类模板)的友元,该类模板在声明的地方必须时可见的。然而,对一个普通类而言,就没有这个要求:

template<typename T>
class Tree{friend class Factory; // 正确:即使这里是Factory的首次声明friedn Class Node<T>; // 如果Node在此是不可见的,那么这条语句就是错误的
};

友元函数

通过确认紧接在友元函数名称后面的是一对<>,我们可以把函数模板的实例声明为友元。<>可以包含模板实参,也可以通过调用参数来演绎出实参,如果全部实参都能够通过演绎获得的话,那么<>里面可以是空的。

template<typename T1, typename T2>
void combine(T1, T2);class Mixer{friend void combine<>(int&, int&);   // 正确: T1 = int&, T2 =int&friend void combine<int, int>(int, int); // 正确: T1 = int, T2 =intfriend void combine<char>(char, int); // 正确: T1 = char, T2 =intfriend void combine<char>(char&, int); //错误:不能匹配上面的combine()模板friend void combine<>(long, long){...}; //错误:这里的友元声明不允许出现定义
};

另外,我们不能在友元声明中定义一个函数模板(我们最多只能定义一个特化);因此,命名一个实例的友元声明是不能作为定义的。

如果名称后面没有紧跟一对<>,那么只有在下面两种情况是合法的:

  • 如果名称不是受限的(也就是说,没有包含域运算符::),那么该名称一定不是(也不能)引用一个模板实例。如果在友元声明的地方,还看不到所匹配的非模板函数(即普通函数),那么这个友元声明就是函数的首次声明。于是,该声明可以是定义。
  • 如果名称是受限的(也就是说,前面包含域运算符::),那么该名称必须引用一个在此之前声明的函数或者函数模板。在匹配的过程中,匹配的函数要优先于匹配的模板函数。注意,这里的友元声明不能是定义。
void multiply(void *) // 普通函数template<typename T> // 函数模板
void multiply(T);class Comrades{friend void multiply(int){};  //可以在一个普通类里面定义一个新的友元函数:multiply(int)非受限函数名称,不能引用模板实例friend void ::multiply(void *) // 引用上面的普通函数而非函数模板friend void ::multiply(int);  //引用一个模板实例friend void ::friend <double *>(double*) //受限名称还可以具有一对<>,但模板在此必须是可见的friend void ::error(){};  // 错误:受限的友元不能是一个定义
}

可以在类模板里面声明友元函数

template<typename T>
class Node{Node<T> *allocate();
};template<typename T>
class List{friend Node<T> * Node<T>::allocate();
};

注意,因为对于任何只在模板内部声明的实体,都要等到模板被实例化之后,才会是一个具体的实体。在这之前该实体是不存在的。类模板的友元函数也是如此:

template<typename T>
class Creator{friend void feed(Creator<T> *){ ... } //每个T都生成一个不同的::feed()函数
};Creator<void> one; //生成::feed(Creator<void>*);
Creator<double> two; //生成::feed(Creator<double>*);

如上,每个Creator实例都生成了一个不同的feed()函数。另外,尽管这些函数是作为模板的一部分被生成的,但函数本身仍然是普通函数,而不是模板实例

最后:由于函数的实体处于类定义的内部,所以这些函数都是内联函数。因此,在两个不同的翻译单元可以生成相同的函数

友元模板

我们通常声明的友元只是:函数模板的实例或者类模板的实例,我们指定的友元也只是特定的实体。然而,有时候需要让模板的所有实例都成为友元,这就需要声明友元模板:

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;
};

和普通友元的声明一样,只有在友元模板声明的是一个非受限的函数名称,并且后面没有紧跟<>的情况下,该友元模板声明才能成为定义

友元模板声明的只是基本模板和基本模板的成员。当进行这些声明之后,与该基本模板相对应的模板局部特化和显示特化都会被自动看成友元

更多推荐

C/C++编程:友元与模板

本文发布于:2024-03-05 14:29:12,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1712545.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:模板

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!