匿名命名空间中模板 class 的朋友

friend of templated class in anonymous namespace

当将 class A 声明为 class B 的友元时,而 A 是在匿名命名空间内定义的,而 B 在外部,一些编译器会产生错误“protected member inaccessible”,而其他编译器不会产生任何错误或警告。如果 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: error #308: function "B<T>::bar [with T=int]" is inaccessible, gcc 7.2: no error, clang 5.0: no error
  2. A 不是模板,而 B 是模板:Intel icc 18:没有错误,gcc 7.2:错误'void B<T>::bar() const [with T = int]' is protected within this context, clang 5: 没有错误
  3. A 是模板,而 B 不是:Intel icc 18: error #308, gcc 7.2: error, clang 5: 没有错误
  4. AB 都不是模板:Intel icc 18:没有错误,gcc 7.2:错误,clang 5:没有错误
  5. AB 都不是模板(如 4.),但 AB 的位置已交换:Intel icc 18:错误 #308, gcc 7.2: 错误, clang 5: 错误: 'bar' is a protected member of 'B'
  6. AB 都是模板(如 1.),但 AB 的位置已交换:Intel icc 18:错误 #308,gcc 7.2:没有错误,clang 5:错误

请参阅编译器资源管理器中的示例:https://godbolt.org/g/6Zdr3c and https://godbolt.org/g/BRqf78 案例 6。

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

据我从标准判断,给定的代码在情况 1-4 中是正确的,并且应该在没有诊断的情况下编译:

未命名命名空间的定义 (7.3.1.1:1) 声明未命名命名空间定义等同于使用翻译单元唯一的名称定义命名命名空间,然后使用 using 指令导入此命名空间。

这将使 A 的声明在全局范围内可访问,并且根据段落 11.3:9

A name nominated by a friend declaration shall be accessible in the scope of the class containing the friend declaration.

因此参数化的 class AB 的朋友并且应该能够访问 B::bar

情况5有区别。据我所知,友元声明在封闭范围内声明了一个新的参数化 class A,这与未命名命名空间中的 class A 不同。