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;
}

为了将这两个函数声明为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;
    }
};

缺点是函数只能通过依赖于参数的查找来调用。在您的示例中这不是问题,但如果他们没有合适的参数,或者您想指定名称而不调用它,则可能会出现问题。

如果您想要一个单独的定义,则必须在类定义之前声明模板(因此它可用于朋友声明),但在之后定义(因此它可以访问类成员)。该类也必须在函数之前声明。这有点乱,所以我只展示两个函数之一:

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;
}

UPDhttp ://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 回答
1

那么有一个解决方案既简单又涉及友元函数的声明和定义之间的分离。在朋友函数的声明中(在类内部),您必须提供与类接受的模板参数不同的模板参数(这很有意义,因为该函数不是此类的成员)。

template<class T>
class MyVar
{
    int x;

    template<typename Type>
    friend void printVar(const MyVar<Type> & var);

    template<typename Type>
    friend void scanVar(MyVar<Type> & var);
};

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

}

template<typename T>
void scanVar(MyVar<T> & var) {

}

无需前向声明,声明与定义分离。

于 2020-07-10T12:09:36.013 回答