繁体   English   中英

在模板类中声明与朋友相同的模板类?

[英]Declare same template class as friend inside the template class?

考虑以下:

template<std::integral T>
class Integer {
  T value;

  template<std::integral T2>
  friend class Integer;

 public:
  template<std::integral T2>
  friend bool operator==(Integer<T> x, Integer<T2> y) {
    return x.value == y.value;
  }
};

Integer类定义了友元operator==来比较其他具有不同模板参数的实体,并将模板Integer类声明为友元类。

但是当我比较两个具有不同模板参数的对象时:

Integer<int> x;
Integer<long> y;
x == y;

Clang 接受它,但 GCC 和 MSVC 拒绝它

<source>:11:25: error: 'long int Integer<long int>::value' is private within this context
   11 |     return x.value == y.value;
      |                       ~~^~~~~

这让我有点吃惊。 在我看来,它应该是格式良好的,因为operator==Integer<int>的朋友,而Integer<long>Integer<int>的朋友,所以operator==也应该有访问成员的权限的Integer<long>

那么我应该信任哪个编译器?

更新

这是CWG1699

[班级.朋友]

10友谊既不是继承的,也不是传递的。 [示例 8:

 class A { friend class B; int a; }; class B { friend class C; }; class C { void f(A* p) { p->a++; // error: C is not a friend of A despite being a friend of a friend } }; class D : public B { void f(A* p) { p->a++; // error: D is not a friend of A despite being derived from a friend } };

— 结束示例]

实例化Integer<int> “注入”以下函数:

template<std::integral T2>
  friend bool operator==(Integer<int> x, Integer<T2> y)

现在,虽然Integer<int>可能是Integer<long>的朋友,但添加的operator==不是 它最多只是Integer<int>的朋友。 所以它不能访问Integer<long>的私有成员。 当然,同样的分析也适用于另一个方向(这并不重要,因为重写的候选人被认为是较差的匹配1 )。

如果你想要混合比较,你需要对称。 两个参数都必须是未知的Integer (具有自己的模板参数),并且类模板需要与运算符成为朋友。

不幸的是,这也意味着操作符不能被内联定义(因为每个实例化都会“注入”并重新定义它)。 所以这是可行的解决方案:

template<std::integral T>
class Integer {
  T value{};

 public:
    template<std::integral T2, std::integral T3>
    friend bool operator==(Integer<T2> x, Integer<T3> y);
};

template<std::integral T2, std::integral T3>
bool operator==(Integer<T2> x, Integer<T3> y) {
    return x.value == y.value;
}

1 over.match.best.general/2.8 - "...一个可行的函数 F1 被定义为比另一个可行的函数 F2 更好的函数,如果 ... F2 是一个重写的候选者 ([over.match.oper]) 并且F1不是”

你朋友的朋友不是你的朋友。

#include <concepts>

template<std::integral T>
class Integer {
  T value;

  template<std::integral T2>
  friend class Integer;

 public:
  template<std::integral T2>
  bool operator==(Integer<T2> y) const {
    return value == y.value;
  }
};

int main() {
  Integer<int> x;
  Integer<long> y;
  return x == y;
}

这是有效的,因为成员operator==已成为朋友。

另一种方法是使用私有子类访问器“key”对象:

struct GetValue {
  template<std::integral T2>
  T operator()(Integer<T2> const& self)const{
    return self.value;
  }
};

作为成员类,它确实可以访问其包含类的友谊。

一旦我们有了这个,那么operator==简单并且可以保持一个免费的友元函数:

template<std::integral T2>
friend bool operator==(Integer<T> x, Integer<T2> y) {
  return GetValue{}(x) == GetValue{}(y);
}

使operator==具有的GetValue “键”对象的能力提供了通过GetValue对象读取不同Integer<T2>的值的能力。

这不是一个很好的答案; @storyteller 在那里做得很好。 这仅仅是解决问题的两种清洁技术。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM