封装命名空间中的模板类为friend

时间:2013-04-02 17:30:36

标签: c++ templates namespaces clang friend

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

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&amp; 4.8,使用-std=c++11)和icpc(13.1),但不是clang(使用-std=c++11 -stdlib=libc++),它抱怨使用非公共构造函数(当即时发现违规方法时)。事实证明,如果朋友声明提供了完整的限定名称,则clang很高兴,如

template<typename> friend class my::A;

这是一个铿锵的错误吗?我原以为封闭的命名空间my中的任何可见符号都可以在内部命名空间my::details中使用而无需进一步限定。 2011标准是什么意思?

1 个答案:

答案 0 :(得分:4)

我认为Clang是对的:

  

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

     

3 在名称空间中首先声明的每个名称都是该名称空间的成员。如果非本地类中的friend声明首先声明了类,函数,类模板或函数模板,则该友元是最内层封闭命名空间的成员。在非命名查找(3.4.1)或限定查找(3.4.3)之前找不到朋友的名称,直到在该命名空间范围内提供匹配声明(在授予友谊的类定义之前或之后)。如果调用了友元函数或函数模板,则可以通过名称查找找到其名称,该名称查找考虑名称空间中的函数和与函数参数类型相关联的类(3.4.2)。如果friend声明中的名称既不合格也不是 template-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