匿名命名空间中模板化类的朋友

时间:2018-01-15 20:55:58

标签: c++ templates namespaces language-lawyer friend

将类A声明为类B的朋友时,虽然A在匿名命名空间内定义,而B在外部定义,但某些编译器会产生错误&# 34; 受保护的成员无法访问"而其他人不会产生任何错误或警告。如果AB或两者都是模板,则情况会发生变化:

namespace {
  template <class T>
  struct A {
    template <class BB>
    void foo(BB const& b) { b.bar(); }
  };
} // end anonymous namespace

template <class T>
class B {
  template <class> friend struct A;

protected:
  void bar() const {}
};

int main() {
  A<int> a;
  a.foo(B<int>{});
}
  1. AB都是模板。然后Intel icc 18:错误 #308: function "B<T>::bar [with T=int]" is inaccessible,gcc 7.2:没有错误,clang 5.0:没有错误
  2. A不是模板而B是模板:英特尔icc 18:没有错误,gcc 7.2:错误'void B<T>::bar() const [with T = int]' is protected within this context,铿锵5:没有错误
  3. A是模板而B不是:英特尔icc 18:错误 #308,gcc 7.2:错误,铿锵5:没有错误
  4. AB都不是模板:Intel icc 18:没有错误,gcc 7.2:错误,clang 5:没有错误
  5. AB都不是模板(如在4.中),但AB的位置已交换:Intel icc 18:错误 #308,gcc 7.2:错误,第5条:错误'bar' is a protected member of 'B'
  6. AB都是模板(如1.),但AB的位置已交换:Intel icc 18:错误 #308,gcc 7.2:没有错误,铿锵5:错误
  7. 有关案例6,请参阅编译器资源管理器中的示例:https://godbolt.org/g/6Zdr3chttps://godbolt.org/g/BRqf78

    那么,正确的行为是什么?哪个编译器是正确的?

1 个答案:

答案 0 :(得分:1)

据我所知,在案例1-4中,给定的代码是正确的,并且应该在没有诊断的情况下进行编译:

未命名的命名空间(7.3.1.1:1)的定义规定,未命名的命名空间定义等同于定义具有转换单元唯一名称的命名命名空间,然后使用using指令导入该命名空间。

这将使A的声明在全球范围内可获得,并根据第11.3:9段

  

朋友声明提名的名称应在包含朋友声明的类的范围内访问。

因此,参数化类AB的朋友,应该能够访问B::bar

在案例5中存在差异。据我所知,friend声明在封闭范围内声明了一个新的参数化类A,它与未命名的命名空间中的类A不同。