C++におけるテンプレートクラスの友元リロード


「プログラマー面接宝典」から引き出された問題です.
テンプレートクラスの友元のリロードを説明して、C++コードで実現しますか?
これは実際に以下のいくつかの問題を考察しています.
1.テンプレートクラスの作成
2.テンプレートクラスの友元関数の作成
3.友元重荷はいつ使いますか?答えは様々なC++の演算子です.最も典型的なのは出力オペレータ<<です.
本の答えは以下の通りです.
#include <iostream>

using namespace std;

template<class T> class Test;

template<class T> ostream & operator<<(ostream & out,const Test<T> &obj);

template<class T> class Test{

	private:
		int num;
	public:
		Test(int n=0){num=n;}

		Test(const Test <T> &copy){num=copy.num;}

		//   “<<”   “<>”          
		friend ostream& operator<< <> (ostream & out,const Test<T> &obj);
};	

template<class T> ostream& operator<<(ostream & out,const Test<T> &obj){
	out<<obj.num;
	return out;
}

int main(){
	Test<int> t(2);
	cout<<t;
	return 0;
}

ただ、上の注釈のどの行がよく分からないので「C++Primer」をめくってみると、この問題について詳しく説明されていることがわかりました.コピー:
クラステンプレートの友元宣言
クラステンプレートには、1つまたは複数のエンティティとの友元関係を宣言する3つの友元宣言が表示されます.
(1)通常の非テンプレートまたは関数の友元宣言で、明確に指定されたクラスまたは関数に友元関係を付与する
(2)クラステンプレートまたは関数テンプレートの友元宣言で、縁のあるすべてのインスタンスへのアクセス権を付与
(2)クラステンプレートまたは関数テンプレートの特定のインスタンスへのアクセスのみを付与する友元宣言
1.普通の友元
非テンプレートクラスまたは非テンプレート関数は、クラステンプレートの友元とすることができます.
template<class Type> class Bar{
	//          
	friend class FolBar;
	friend void fcn();
};
この宣言は、FolBarのメンバーとfcn関数がBarクラスの任意のインスタンスのpriveteメンバーとprotectedメンバーにアクセスできるということです.
2.一般テンプレート友元
友元はクラステンプレートまたは関数テンプレートです.
template<class Type> class Bar{
	//   Foo1 temp1_fcn1     
	template<class T> friend class Foo1;
	template<class T> friend void temp1_fcn1(const T&);
};

これらの友元宣言では、Foo 1とtemp 1_を指すクラス自体とは異なるタイプのパラメータが使用されます.fcn 1のタイプパラメータ.どちらの場合も,数制限のないクラスと関数をBarの友元とする.Foo 1の友元宣言は、Foo 1の任意のインスタンスがBarの任意のインスタンスのプライベートメンバーにアクセスできることを意味し、同様にtemp 1_fcn 1の任意のインスタンスはBarの任意のインスタンスにアクセスすることができる.
この友元声明はBarとその友元Foo 1とtemp 1で_fcn 1の各インスタンス間では、一対のマルチマッピングが確立される.Barの各インスタンスについて、Foo 1またはtemp 1_fcn 1のすべてのインスタンスは友元である.
3.特定のテンプレートの友元関係
1つのテンプレートのすべてのインスタンスを友元に設定するだけでなく、クラスは特定のインスタンスへのアクセス権のみを付与することもできます.
template<class T> class Foo2;
	template<class T> void temp1_fcn2(const T&);
	template<class Type> class Bar{
		//         char*   
		friend class Foo2<char *>;
		friend void temp1_fcn2<char *>(char * const &);
	};
Foo 2自体がクラステンプレートであっても、友元関係はFoo 2のパラメータタイプchar*の特定のインスタンスにのみ拡張される.同様にtemp 1_fcn 2の友元宣言は,パラメータタイプchar*の関数インスタンスのみがBarクラスの友元であるという.パラメータタイプchar*のFoo 2とtemp 1_fcn 2の特定のインスタンスはBarの各インスタンスにアクセスすることができる.
次の形式の友元宣言は、より一般的です.
template<class T> class Foo3;
	template<class T> void temp1_fcn3(const T&);
	template<class Type> class Bar{
		//Bar               Bar   Foo3 temp1_fcn3   
		friend class Foo3<Type>;
		friend void temp1_fcn3<Type>(const Type &);
	};
これらの友元は、Barの特定のインスタンスと、同じテンプレートの実パラメータを使用するFoo 3またはtemp 1_を定義する.fcn 3のインスタンス間の友元関係,各Barインスタンスには関連するFoo 3とtemp 1_がある.fcn 3友元:
	Bar<int> b1;//     Foo3<int> temp1_fcn3<int>
	Bar<string> bs;//     Foo3<string> temp1_fcn3<string>

与えられたBarインスタンスと同じテンプレート実パラメータを持つFoo 3またはtemp 1_のみfcn 3バージョンは友元です.したがって、Foo 3は、Barのプライベート部分にアクセスすることができるが、Barまたは任意の他のBarインスタンスのプライベート部分にアクセスすることはできない.
4.依存性の宣言
指定されたテンプレートのすべてのインスタンスにアクセス権を付与する場合、クラステンプレートまたは関数テンプレートの宣言は、役割ドメインに存在する必要はありません.実質的に、コンパイラは、友元宣言もクラスまたは関数の宣言として扱われます.
特定のインスタンス化の友元関係を制限するには、友元宣言に使用できる前にクラスまたは関数を宣言する必要があります.
template <class T> class A;
template <class T> class B{
	public:
		friend class A<T>;//ok,A    
		friend class C;//ok,C      
		template<class S> friend class D;//ok,D     ,    D       
		friend class E<T>;//error,    
		friend class F<int>;//error,    
};

g++でこのエラーが発生します.
コンパイラが友元がテンプレートであることを事前に通知していない場合、コンパイラは友元が通常の非テンプレートクラスまたは非テンプレート関数であると判断します.
上は「C++Primer」のテンプレート類の友元についての説明ですが、これらを理解してからこの問題を見ると分かりやすいです.第3点によれば,特定のテンプレートの友元関係はなぜ<>が必要なのか,実際にはであるが,<>のみと書くことができる.
4点目のタイプ依存性から,なぜ一番上に声明を付ける必要があるのかが分かる.
次に,2点目の一般テンプレートの友元関係に従って書き換えることができ,コードは以下の通りである.しかし,その必要はないと感じる.
上記の4点目に従って、すべてのインスタンスにアクセスする場合、事前に宣言する必要はありませんので、前の宣言は必要ありません.
<<後ろも<>いらない.その前にtemplateというキーワードが付いているからです.
注意:後でクラステンプレートまたは関数テンプレートから<>という記号が漏れるのを防止するために、クラステンプレートまたは関数テンプレートについてはtemplate修飾があるか、<>が必要であるかのどちらかを記憶することができます.
#include <iostream>

using namespace std;

template<class T> class Test{

	private:
		int num;
	public:
		Test(int n=0){num=n;}

		Test(const Test <T> &copy){num=copy.num;}

		//   “<<”   “<>”          
		template<class TT> friend ostream& operator<< (ostream & out,const Test<TT> &obj);
};	

template<class T> ostream& operator<<(ostream & out,const Test<T> &obj){
	out<<obj.num;
	return out;
}




int main(){
	Test<int> t(2);
	cout<<t;
	return 0;
}