模板类的模板友元函数

template friend functions of template class

本文关键字:函数 友元      更新时间:2023-10-16

我有下面的模板类和模板函数,它们打算访问类的私有数据成员:

#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

最简单的选择是在类中定义友元:

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

我设法得到了以下工作

#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>

这样的操作符可以在类体中定义,其作用是为每个T和生成一个单独的非模板operator<<使非模板operator<<成为它的Foo<T>的朋友

或函数模板必须在在这种情况下,Foo<T>中的友元声明可以关于T

,请参阅operator<<的完全专门化

这个在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;
}

有一个解决方案既简单又涉及到声明&好友函数的定义。在友元函数的声明中(在类内部),你必须给出与类接受的模板参数不同的模板参数(这是有意义的,因为这个函数不是这个类的成员)。

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) {
}

不需要前向声明,并且有一个声明&div定义。