具有模板类友元的类模板,这里到底发生了什么?

97
假设我正在创建一个二叉树的类,名称为 BT,并且我有一个描述树元素的类,BE,类似于:
template<class T> class BE {
    T *data;
    BE *l, *r;
public:
...
    template<class U> friend class BT;
};

template<class T> class BT {
    BE<T> *root;
public:
...
private:
...
};

这似乎是有效的;然而,我对底层发生的事情有疑问。

最初我尝试将朋友声明为

template<class T> friend class BT;

然而,似乎有必要在这里使用U(或其他不同于T的东西),为什么?这是否意味着任何特定的BT都是某个特定的BE类的朋友?

IBM关于模板和友元的页面提供了不同类型函数的友元关系示例,但没有提供类的示例(并且猜测语法还没有收敛于解决方案)。我更希望了解如何正确地获取我想定义的友元关系类型的规范。

5个回答

137
template<class T> class BE{
  template<class T> friend class BT;
};

不允许这样做,因为模板参数不能相互遮蔽。嵌套的模板必须有不同的模板参数名称。

template<typename T>
struct foo {
  template<typename U>
  friend class bar;
};

这意味着无论 bar 的模板参数是什么,它都是 foo 的朋友。bar<char>bar<int>bar<float> 和任何其他的 bar 都将成为 foo<char> 的朋友。

template<typename T>
struct foo {
  friend class bar<T>;
};
这意味着当bar的模板参数与foo的匹配时,barfoo的友元。只有bar<char>会成为foo<char>的友元。
在您的情况下,friend class bar<T>; 应该就足够了。

2
在我的代码中,这个构造friend class BT对于友元行抛出错误error: 'BT' 不是一个模板尽管它稍后被声明为:template class BT { ... } - Michael Conlen
3
所以秘密就在于我需要提前声明BT,这样才能在BE中使用friend class BT<T>;这一行,但不需要为template<class U> friend class BT;进行提前声明。感谢帮助! - Michael Conlen
26
更具体地说,您需要在BE类的定义之前进行template<typename T> class BT;的前向声明,然后在BE类内部使用friend class BT<T>;。请注意,不要改变原来的意思。 - Bartosz Milewski

13
为了与另一个相同类型的结构体建立友好关系:
#include <iostream>

template<typename T_>
struct Foo
{
    // Without this next line source.value_ later would be inaccessible.
    template<typename> friend struct Foo;

    Foo(T_ value) : value_(value) {}

    template <typename AltT>
    void display(AltT &&source) const
    {
        std::cout << "My value is " << value_ << " and my friend's value is " << source.value_ << ".\n";
    }

protected:
    T_ value_;
};

int main()
{
    Foo<int> foo1(5);
    Foo<std::string> foo2("banana");

    foo1.display(foo2);

    return 0;
}

以下是输出结果:
My value is 5 and my friend's value is banana. 

template<typename>中,友元结构Foo不应该在typename/class后面写T,否则会导致模板参数阴影错误。

正是我所寻找的!;) - jihlim

4

如果进行重构,不需要为参数命名,这样可以减少故障点:

     template <typename _KeyT, typename _ValueT> class hash_map_iterator{
       template <typename, typename, int> friend class hash_map;
       ...

0

使一个模板类成为另一个模板类的友元的最佳方法如下:

#include <iostream>
using namespace std;

template<typename T>
class B;

template<typename T>
class A
{
   friend class B<T>;
private:
   int height;
public:
   A()//constructor
   A(T val) //overloaded constructor

};

template<typename T>
class B
{
private:
   ...
public:
   B()//constructor
   B(T val) //overloaded constructor
};

这使得特定的专业化“B<T>”成为专业化“A<T>”的朋友。模板本身无法成为友元,虽然最接近这个意思的是使得一个模板的所有专业化都成为另一个模板的所有专业化的友元(然后friend声明必须为模板 - 请参见被接受的答案)。 - YurkoFlisk

-5
在我的情况下,这个解决方案可以正确地工作:
template <typename T>
class DerivedClass1 : public BaseClass1 {
  template<class T> friend class DerivedClass2;
private:
 int a;
};

template <typename T>
class DerivedClass2 : public BaseClass1 {
  void method() { this->i;}
};

我希望这会有所帮助。


7
当你遮蔽模板参数并且没有使用DerivedClass2对DerivedClass1的友元访问权限时,这个解决方案应该如何正确运行? - kornman00

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接