5

クラスのプライベート データ メンバーにアクセスするための次のテンプレート クラスとテンプレート関数があります。

#include <iostream>

template<class T>
class MyVar
{
    int x;
};

template<class T>
void printVar(const MyVar<T>& var)
{
    std::cout << var.x << std::endl;
}

template<class T>
void scanVar(MyVar<T>& var)
{
    std::cin >> var.x;
}

struct Foo {};

int main(void)
{
    MyVar<Foo> a;
    scanVar(a);
    printVar(a);
    return 0;
}

のフレンド関数として 2 つの関数を宣言するために、フレンドシップを宣言するためMyVar<T>に の宣言内で次の方法を試しましたtemplate<class T> class MyVar。それらのどれも機能しません。どうすればいいですか?

template<class T> friend void printVar(const MyVar&);
template<class T> friend void scanVar(MyVar&);
// compilation error

template<class T> friend void printVar(const MyVar<T>&);
template<class T> friend void scanVar(MyVar<T>&);
// compilation error

friend void printVar(const MyVar<T>&);
friend void scanVar(MyVar<T>&);
// link error

friend void printVar(const MyVar&);
friend void scanVar(MyVar&);
// link error too
4

4 に答える 4

5

最も簡単なオプションは、クラス内でフレンドを定義することです。

template<class T>
class MyVar
{
    int x;

    friend void printVar(const MyVar & var) {
        std::cout << var.x << std::endl;
    }
    friend void scanVar(MyVar & var) {
        std::cin >> var.x;
    }
};

欠点は、関数が引数依存のルックアップを介してのみ呼び出されることです。これはあなたの例では問題ではありませんが、適切な引数がない場合、または名前を呼び出さずに指定したい場合に問題になる可能性があります。

別の定義が必要な場合は、テンプレートをクラス定義の前に宣言する必要がありますが (フレンド宣言で使用できるようにするため)、後で定義する必要があります (クラス メンバーにアクセスできるようにするため)。クラスも関数の前に宣言する必要があります。これは少し面倒なので、2 つの関数のうちの 1 つだけを示します。

template <typename T> class MyVar;
template <typename T> void printVar(const MyVar<T> & var);

template<class T>
class MyVar
{
    int x;

    friend void printVar<T>(const MyVar<T> & var);
};

template <typename T> void printVar(const MyVar<T> & var) {
    std::cout << var.x << std::endl;
}
于 2015-05-25T13:58:47.947 に答える
2

私は次の仕事を得ることができました

#include <iostream>

template<class T>
class MyVar;

template<class T>
void printVar(const MyVar<T>& var);

template<class T>
void scanVar(MyVar<T>& var);

template<class T>
class MyVar
{
    int x;
    friend void printVar<T>(const MyVar<T>& var);
    friend void scanVar<T>(MyVar<T>& var);
};

template<class T>
void printVar(const MyVar<T>& var)
{
    std::cout << var.x << std::endl;
}

template<class T>
void scanVar(MyVar<T>& var)
{
    std::cin >> var.x;
}

struct Foo {};

int main(void)
{
    MyVar<Foo> a;
    scanVar(a);
    printVar(a);
    return 0;
}

UPD : http://en.cppreference.com/w/cpp/language/friendは、「テンプレート フレンド オペレーター」の下のオペレーターと同様のケースについて語っています。

operator<<(std::ostream&, const Foo<T>&)テンプレート フレンドの一般的な 使用例は、クラス テンプレートで動作する非メンバー オペレーター オーバーロードの宣言です。 Foo<T>

operator<<このような演算子は、クラス本体で定義できます。これにより、それぞれに個別の非テンプレートを生成し、Tその非テンプレートoperator<<をそのフレンドにする効果があります。Foo<T>

...

または、関数テンプレートをクラス本体の前にテンプレートとして宣言する必要があります。この場合、内部のフレンド宣言は、そのFoo<T>完全な特殊化を参照できます。operator<<T

于 2015-05-25T13:53:26.093 に答える
1

これは MSVC2013 でコンパイルされます。基本的に、フレンドの前にクラスと関数に前方宣言を追加します

template<class T>   class MyVar ; // class forward declaration

template<class T> ; // function forward declarations
void printVar(const MyVar<T>& var);
template<class T>
void scanVar(MyVar<T>& var);

template<class T>
class MyVar
{
    friend void printVar<T>(const MyVar<T>&);
    friend void scanVar<T>(MyVar<T>&);
    int x;
};

template<class T>
void printVar(const MyVar<T>& var)
{
    std::cout << var.x << std::endl;
}

template<class T>
void scanVar(MyVar<T>& var)
{
    std::cin >> var.x;
}

struct Foo {};

int main1(void)
{
    MyVar<Foo> a;
    scanVar(a);
    printVar(a);
    return 0;
}
于 2015-05-25T13:55:04.997 に答える