この質問で説明されている問題 (テンプレート関数をテンプレート クラスのフレンドとして宣言する) に苦労していましたが、2 番目の答えは私がやりたいことだと思います (テンプレート関数を前方宣言し、特殊化をフレンドとして指定します)。 )。わずかに異なる解決策が実際に正しいのか、それとも Visual C++ 2008 でたまたま機能するのかについて質問があります。
テストコードは次のとおりです。
#include <iostream>
// forward declarations
template <typename T>
class test;
template <typename T>
std::ostream& operator<<(std::ostream &out, const test<T> &t);
template <typename T>
class test {
friend std::ostream& operator<< <T>(std::ostream &out, const test<T> &t);
// alternative friend declaration
// template <typename U>
// friend std::ostream& operator<<(std::ostream &out, const test<T> &t);
// rest of class
};
template <typename T>
std::ostream& operator<<(std::ostream &out, const test<T> &t) {
// output function defined here
}
まず、私が見つけた奇妙なことの 1 つは、の前方宣言を変更してoperator<<
一致しないようにすると (たとえば、std::ostream& operator<<(std::ostream &out, int fake);
すべてが正しくコンパイルされ、正しく動作することでした (明確にするために、そのような関数を定義する必要はありません。ただし、リンク先の質問のように、前方宣言を削除すると、コンパイラがフレンド関数ではなくデータ メンバーを宣言していると考えているように見えるため、問題が発生します。 Visual C++ 2008 のバグ。
興味深いのは、上記のコードで forward 宣言を削除し、代替の friend 宣言を使用する場合です。テンプレート パラメーターU
は、次の署名には表示されないことに注意してください。このメソッドもコンパイルされ、正しく動作します (他に何も変更する必要はありません)。私の質問は、これが標準に準拠しているのか、それとも Visual C++ 2008 の特異性に準拠しているのかということです (参考書で適切な答えを見つけることができませんでした)。
フレンド宣言template <typename U> friend ... const test<U> &t);
も機能しますが、実際にはこれにより、オペレーターの各インスタンスが のfriend
任意のインスタンスにアクセスできるようになりますがtest
、私が望むのは、 のプライベート メンバーにtest<T>
は からのみアクセスできるようにすることoperator<< <T>
です。test<int>
内部でをインスタンス化しoperator<<
、プライベート メンバーにアクセスして、これをテストしました。を出力しようとすると、コンパイル エラーが発生するはずtest<double>
です。
概要: 上記のコードで forward 宣言を削除して代替の friend 宣言に切り替えると、(Visual C++ 2008 の場合) 同じ結果になるようです -- このコードは実際に正しいのでしょうか?
更新: コードに対する上記の変更はいずれも gcc では機能しないため、これらは Visual C++ コンパイラのエラーまたは「機能」であると推測しています。それでも、標準に精通している人々からの洞察をいただければ幸いです。