类模板的嵌套类中的友元声明(friend declarations in a class nested inside a class template)
◇ (for Possibility)
- 章节名:类模板的嵌套类中的友元声明(friend declarations in a class nested inside a class template)
日志无法插代码,弄到这里来好了。也确实引用了这边的不少内容w C++ Primer上有说类模板的嵌套类,有说类模板的友元声明,但是没有说嵌套类中的友元声明(如果不是我没找到的话……)。 最开始的写法如下,是想把Linked_list类的成员函数NodePtr_to_iter和iter_to_NodePtr设为嵌套类iter的友元:
template <typename T> class Linked_list { protected: struct Node { //... }; class iter //iter is implemented in terms of Node*, and supports arithmetic operations { public: iter() : ptr(NULL) {} //... private: explicit iter(Node* nP) : ptr(nP) {} private: Node *ptr; //friends: friend iter NodePtr_to_iter(Node* const nP); friend Node* iter_to_NodePtr(const iter it); }; iter NodePtr_to_iter(Node* const nP) { return iter(nP); } Node* iter_to_NodePtr(const iter it) { return it.ptr; } public: //... protected: //... };结果是编译不通过,成员不可访问。 ICC: warning #1624: "Linked_list<T>::iter NodePtr_to_iter(Linked_list<T>::Node *)" declares a non-template function -- add <> to refer to a template instance error #373: "Linked_list<T>::iter::iter(Linked_list<T>::Node *) [with T=Term]" (declared at line 55) is inaccessible VC++: error C2248: 'Linked_list<T>::iter::iter' : cannot access private member declared in class 'Linked_list<T>::iter' 原因C++P上有说——
友元声明中不带template关键字(将访问权授予模板)或限定符<>(将访问权授予模板的特定实例)的时候,实际是[b]将访问权授予了一个非模板[b],但是[b]模板类的成员也是模板[b],所以这里被授予友元关系的函数NodePtr_to_iter 与 Linked_list的成员函数NodePtr_to_iter实际上不是一个函数,所以成员函数NodePtr_to_iter并没有获得iter的私有成员的访问权。(事实上还不仅如此,见下文) 之后改成:
template <typename T> class Linked_list { protected: struct Node { //... }; class iter //iter is implemented in terms of Node*, and supports arithmetic operations { public: iter() : ptr(NULL) {} //... private: explicit iter(Node* nP) : ptr(nP) {} private: Node *ptr; //friends: friend iter NodePtr_to_iter(Node* const nP) { return iter(nP); } friend Node* iter_to_NodePtr(const iter it) { return it.ptr; } }; public: typedef iter iterator; //... protected: //... };这回编译通过而且测试能够实现预期的功能。 但还是有问题。 我们知道,定义在类体内部的友元函数的作用域其实是被引入到了外围作用域的:
于是直接的想法是这里NodePtr_to_iter和iter_to_NodePtr被引入到外围类Linked_list并成为成员函数,但不要忘了:模板类的成员也是模板[b]且友元声明中不带template关键字(将访问权授予模板)或限定符<>(将访问权授予模板的特定实例)的时候,实际是[b]将访问权授予了一个非模板[b]。这不是矛盾了吗!? 好吧,实际上是:NodePtr_to_iter和iter_to_NodePtr被引入到了[b]全局作用域中了。而且要注意这里的友元声明确实不是对于函数模板的,而是,每个Linked_list的实例都会定义一个NodePtr_to_iter和一个iter_to_NodePtr(类型由Linked_list的模板实参决定)并引入到全局作用域。 不仅在VS的class view中可以看出是如此,而且在全局作用域中也确实可以引用iter_to_NodePtr并编译通过。 但又遇到一个问题,在全局作用域中写如下代码测试:
Linked_list<char>::Node* pNode = iter_to_NodePtr(list.find('w')); //returned type of find() is iteratorVC++编译不通过: error C2248: 'Linked_list<T>::Node' : cannot access protected struct declared in class 'Linked_list<T>' ICC编译通过且工作正常,但有警告: warning #525: class "Linked_list<T>::Node [with T=char]" (declared at line 10 of "...\Linked_list.h") is an inaccessible type (allowed for cfront compatibility) 受保护的Linked_list<T>::Node现在可以访问了!!不过提示中的(allowed for cfront compatibility)很让人在意,看起来这是一个已经被废弃的特性。 ////////////////////////////////////////////////!!!怒球相关资料!!!//////////////////////////////////////////////// 接下来是得到正确写法的过程。 比较难办的问题是:外围类的成员函数同时也是函数模板,同时,我们只想将iter<T>(某个实例)的友元关系授予给Linked_list<T>::NodePtr_to_iter(具有相同实参的实例)。但无论如何,因为:
前者似乎要求外围类Linked_list必须在嵌套类iter之前定义,这当然是不可能的!而后者要求NodePtr_to_iter和iter_to_NodePtr在iter之前声明。好吧,我们至少可以满足后者。现在的问题NodePtr_to_iter和iter_to_NodePtr当成是函数模板还是外围类Linked_list的成员函数(同时也是函数模板)?我们不妨都试试:
template <typename T> class Linked_list { protected: struct Node { //... }; class iter; iter NodePtr_to_iter(Node* const nP); Node* iter_to_NodePtr(const iter it); class iter //iter is implemented in terms of Node*, and supports arithmetic operations { public: iter() : ptr(NULL) {} //... private: explicit iter(Node* nP) : ptr(nP) {} private: Node *ptr; //friends: friend iter Linked_list::NodePtr_to_iter(Node* const nP); friend Node* iter_to_NodePtr<T>(const iter it); }; public: //... protected: //... };于是我们收到了了来自编译器的友情提示: error : iter_to_NodePtr is not a template error #308: member "Linked_list<T>::iter::ptr [with T=Term]" (declared at line 59 of "...\Linked_list.h") is inaccessible 事后想来确实合情合理,在其他地方我们也从来都不能通过显式实参直接指定类模板成员函数的实例,而是通过类模板实例的作用域限定符来指定的。 同时我们还知道了:嵌套类将外部类的成员函数设为友元,只要该成员函数的声明在嵌套类定义之前就可。同时需要注意的是因为成员函数的声明用到了iter,所以iter应该在之前做前向声明。 于是我们得到最后的正确写法(加上static是为了满足其它需要):
template <typename T> class Linked_list { protected: struct Node { //... }; class iter; static iter NodePtr_to_iter(Node* const nP); static Node* iter_to_NodePtr(const iter it); class iter //iter is implemented in terms of Node*, and supports arithmetic operations { public: iter() : ptr(NULL) {} //... private: explicit iter(Node* nP) : ptr(nP) {} private: Node *ptr; //friends: friend iter Linked_list::NodePtr_to_iter(Node* const nP); friend Node* Linked_list::iter_to_NodePtr(const iter it); }; public: //... protected: //... }; template <typename T> inline typename Linked_list<T>::iter Linked_list<T>::NodePtr_to_iter(Node* const nP) { return iter(nP); } template <typename T> inline typename Linked_list<T>::Node* Linked_list<T>::iter_to_NodePtr(const iter it) { return it.ptr; }最后需要注意的是因为NodePtr_to_iter和iter_to_NodePtr需要访问iter的成员,所以只能在iter之后定义,也就是在类外定义(类成员在类内部只能声明一次,定义也是声明)。
说明 · · · · · ·
表示其中内容是对原文的摘抄