作为友元的封闭命名空间中的模板类

template class in enclosing namespace as friend

本文关键字:命名空间 友元      更新时间:2023-10-16

我的代码具有以下基本结构

namespace my {
  template<typename> class A;              // forward declaration
  namespace details {
    template<typename T> class B
    {
      const T*const t;
      B(const T*x) : t(x) {}               // non-public constructor
      template<typename> friend class A;   // friend declaration
    };
  }
  template<typename T> class A
  {
    T*const t;
  public:
    A(T*x) : t(x) {}
    details::B<T> makeb() const            // offending method:
    { return details::B<T>(t); }           //   calls non-public constructor
  };
}

在GCC (4.7 &4.8,使用-std=c++11)和icpc(13.1),但没有clang(使用-std=c++11 -stdlib=libc++),它抱怨使用非公共构造函数(当实例化违规方法时)。如果友元声明给出完整的限定名,如

, clang就会很高兴。
template<typename> friend class my::A;

这是clang的bug吗?我本以为封闭命名空间my中的任何可见符号都可以使用,而无需在内部命名空间my::details中进一步限定。2011年的标准是怎么说的?

我认为Clang是对的:

7.3.1.2命名空间成员定义[namspace .memdef]

3在命名空间中首先声明的每个名称都是该命名空间的成员。如果非局部类中的friend声明首先声明了一个类、函数、类模板或函数模板,则友元是最内层封闭命名空间的成员。非限定查找(3.4.1)或限定查找(3.4.3)不会找到友元的名称,直到在该命名空间作用域中提供了匹配声明(在授予友元的类定义之前或之后)。如果调用友元函数或函数模板,则可以通过名称查找来查找其名称,该查找考虑与函数实参类型相关的命名空间和类中的函数(3.4.2)。如果friend声明中的名称既不是限定的,也不是模板id,并且声明是函数或详细类型说明符,则查找确定实体是否先前声明过,不应考虑最内层封闭命名空间之外的任何作用域。[注意:其他形式的友元声明不能声明最内层封闭命名空间的新成员,因此不能遵循通常的查找规则。- 结束说明] [示例:

// Assume f and g have not yet been declared.
void h(int);
template <class T> void f2(T);
namespace A {
  class X {
    friend void f(X);         // A::f(X) is a friend
    class Y {
      friend void g();        // A::g is a friend
      friend void h(int);     // A::h is a friend
                              // ::h not considered
      friend void f2<>(int);  // ::f2<>(int) is a friend
    };
  };
  // A::f, A::g and A::h are not visible here
  X x;
  void g() { f(x); }          // definition of A::g 
  void f(X) { /* ...  */}     // definition of A::f 
  void h(int) { /* ...  */ }  // definition of A::h
  // A::f, A::g and A::h are visible here and known to be friends
}
using A::x;
void h() {
  A::f(x);
  A::X::f(x);      // error: f is not a member of A::X
  A::X::Y::g();    // error: g is not a member of A::X::Y
}

- 结束示例]

注意// ::h not considered