关于C ++:如何使模板类的一个实例与同一模板的另一个实例成为好朋友

How to befriend one instantiation of template class with other one of the same template

我有一个模板类,该模板类仅将模板参数用于用户界面功能,并在示例中将数据内部保存在某些字段中,例如ii的类型未模板化。所有实例都必须协同工作并可以访问i,但不能从模板用户外部访问i
问题在于如何使朋友成为同一模板的不同实例。他们必须访问其他实例化的私有数据,并且在内部所有这些实例化都是同一回事。

假设我想像下面的示例一样比较模板的不同实例。这里用于与其他模板参数实例化的i是私有的,如果我尝试声明此模板friend,它将无法正常工作。
我知道,我可以在类模板之外进行operator ==的定义,但是假设我需要执行某些功能,例如is_same,该函数必须是成员,并且我希望它的主体位于类声明的内部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>

template<typename T>
class C
{
    template<typename U> // ????????
    friend class C;

    template<typename U>
    friend bool operator == (C const& c1, C<U> const& c2) noexcept
    {
      return c1.i == c2.i;
    }

    int i;

  public:

    C(): i {0} {}
    C(int val): i {val} {}

    template<typename U>
    bool is_same(C<U> const& c)
    {
      return c.i == i;
    }

}; // template class C

int main()
{
  C<int>  c1 {5};
  C<char> c2 {5};

  if (c1 == c2) std::cout <<"All ok" << std::endl;
  else          std::cout <<"Very bad" << std::endl;

  if (c1.is_same(c2)) std::cout <<"All ok" << std::endl;
  else                std::cout <<"Very bad" << std::endl;

  return 0;
}

编译错误(gcc 5.1 -std = c ++ 14)

1
~/main.cpp:15:9: error: ?int C<char>::i? is private


那么为什么不使用类内部进行编译,
并在课外进行编译。

当你有这个:

1
2
3
4
5
6
7
8
  template <typename T> struct C {
  template<typename U>
  friend bool operator == (C const& c1, C<U> const& c2)
  {
    return c1.i == c2.i;
  }
  };
  C<int>() == C<char>()

class C的外部范围内生成了两个::operator==,在我们的示例中是全局名称空间:::operator==(C const& c1, C const& c2)(1)和
::operator==(C const& c1, C const& c2)(2),第一个是C的朋友,第二个是C的朋友。 并且编译器使用(1)。 (1)是C的唯一朋友,因此编译时错误。

但是,如果您编写这样的代码:

1
2
3
4
5
6
7
8
9
10
11
12
template <typename T> struct C {
    template<typename U1, typename U>
    friend bool operator == (C<U1> const& c1, C<U> const& c2);
private:
    int i;
};

template<typename U1, typename U>
bool operator == (C<U1> const& c1, C<U> const& c2)
{
    return c1.i == c2.i;
}

其中operator==不在C内部实现,则编译器不会在其已使用的类C范围之外生成operator==
因此我们有::operator==C的哪个朋友。 最后,当您拥有:

1
2
3
4
5
6
7
8
template <typename T> struct C {
    template<typename U1, typename U>
    friend bool operator == (C<U1> const& c1, C<U> const& c2) {
        return c1.i == c2.i;
    }
private:
    int i;
};

如果是C() == C(),则有两个由::operator== ::operator==(Cconst&, Cconst&)的编译器变体生成,并且
::operator==(Cconst&, Cconst&)和编译器应在此处生成编译时错误。

参考c ++ 11标准最新草案,第14.5.4和11.3.5节。