1. 程式人生 > >C++中模板類使用友元模板函式

C++中模板類使用友元模板函式

問題始於學習資料結構,自己編寫一個單鏈表,其中用到了過載輸出運算子<<,我寫的大約這樣:

template <class T> 
class List
{
    friend std::ostream& operator << (std::ostream& os,const List<T>& slist);
    //……
};

用vs2008可編譯,但無法連結:無法解析的外部符號

後來上網查改為

template <class T> 
class List
{
    friend std::ostream&
operator << <>(std::ostream& os,const List<T>& slist); //…… };

就可以了。不知所以然,查了下《C++ Primer》才弄明白。
好了,進入正題:
在類模板中可以出現三種友元宣告:
(1)普通非模板類或函式的友元宣告,將友元關係授予明確指定的類或函式。
(2)類模板或函式模板的友元宣告,授予對友元所有例項的訪問權。
(3)只授予對類模板或函式模板的特定例項的訪問權的友元宣告。
要注意的是,友元函式並非成員函式,是改變了它對類成員的訪問許可權。
(1)沒有什麼好說的,如:

template<class T>
class A
{
   friend void fun();
//...
};

此例中fun可訪問A任意類例項中的私有和保護成員
(2)

template<class T>
class A
{
   template<class T>
   friend void fun(T u);
//...
};

這時友元使用與類不同的模板形參,T可以是任意合法標誌符,友元函式可以訪問A類的任何類例項的資料,即不論A的形參是int,double或其他都可以。
(3)

template<class T>
class A
{
   friend void fun<T
>(T u); //... };

此時fun只有訪問類中特定例項的資料。換句話說,此時具有相同模板實參的fun函式與A類才是友元關係。即假如呼叫fun時其模板實參為int,則它只具有A的訪問許可權。當然friend void fun(T u);中<>中的T可以是任意型別,比如int,double等
回到原問題,按(3)可改為:

template <class T> 
class List
{
    friend std::ostream& operator << <T>(std::ostream& os,const List<T>& slist);
    //……
};

按(2)可改為:

template <class T> 
class List
{
    template <class T>
    friend std::ostream& operator << (std::ostream& os,const List<T>& slist);
    //……
};

在這裡其實兩者實現的最終效果一樣的,因為呼叫輸出運算子時需要訪問的類例項的物件是它本身,所以形參T在第一種改法中一定匹配。

對類建立友元函式很容易。但是遷移到模板上卻容易出現讓人摸不著頭腦的連線錯誤。
層次不夠,不做分析,單純介紹兩種為類模板定義友元函式的方法

1 封閉型

template< typename T >
class MyClass
{
    friend void function( MyClass< T > &arg )
    {
          .... 
    }
};

要點:友元函式定義在模板體內。

2 開放型

template< typename T >
class MyClass
{
    template< typename C >
    friend void function( MyClass< C > &arg );
};

template< typename C >
void function( MyClass< C > &arg )
{
    ....
}

要點:模板體內要另建模板。

3 告訴編譯器宣告的設個是模板

#include <iostream>
using namespace std;

template < typename T >
class A
{
    friend ostream &operator<< < T >( ostream &, const A< T > & );
};

template < typename T >
ostream &operator<< ( ostream &output, const A< T > &a )
{
    output << "過載成功" << endl;
    return output;
}

int main()
{
    A< int > a;
    cout << a;
}

要點:顯示地在過載的運算子或者函式後面加上模板宣告< T >,告訴編譯器友元函式是一個型別一致的模板。

建議:
如果希望使用函式與模板特化的型別相對應,則使用方法3(模板顯示宣告)
如果希望使用函式與模板特化的型別相獨立,則使用方法2(二重模板)
簡短的行內函數使用方法1