clang bug?命名空间模板类的朋友

Mic*_*iak 13 c++ g++ language-lawyer clang++

下面的代码不是在clang下编译但是在gcc和VS下编译:

template<typename T> class bar;

namespace NS
{
    template<typename T>
    class foo
    {
        foo() {}

        template<typename U> friend class bar;
    };
}

template<typename R>
class bar
{
public:
    bar()
    {
        NS::foo<int> f;
    }
};


int main(int, char **)
{
    bar<int> b;        
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它失败了:

main.cpp:20:22: error: calling a private constructor of class 'NS::foo<int>'

        NS::foo<int> f;    
                     ^

main.cpp:8:9: note: implicitly declared private here

        foo() {}   
        ^
Run Code Online (Sandbox Code Playgroud)

bar应该有权访问foo私有构造函数但看起来却没有.如果我删除namespace NS,它编译.

代码看起来很好,但也许我误解了C++标准.哪个编译器正确?

Bar*_*rry 14

我相信铿锵是正确的.根据[namespace.memdef]/3:

首先在名称空间中声明的每个名称都是该名称空间的成员.如果friend非本地类中的声明首先声明了类,函数,类模板或函数模板,则该友元是最内层封闭命名空间的成员.

在您的情况下,该名称似乎不会被声明"首次声明" friend.然而,在该段后面强调我的:

如果friend声明中的名称既不是限定名也不是模板标识,并且声明是函数或详细类型说明符,则确定实体是否先前已声明的查找不应考虑最内层封闭命名空间之外的任何范围.

也就是说,这个声明:

template<typename U> friend class bar;
Run Code Online (Sandbox Code Playgroud)

不会寻找bar外面的namespace NS,所以它不会找到你早先的声明.因此,它声明一个类模板NS::bar<typename >是一个friendfoo.您必须对该名称进行限定bar才能找到它:

template<typename U> friend class ::bar;
Run Code Online (Sandbox Code Playgroud)

这似乎与GCC Bug 37804有关.


vso*_*tco 5

cppreference:

由非本地类X中的友元声明引入的名称成为X的最内层封闭命名空间的成员,但它们不会对查找可见(既不是非限定的也不是限定的),除非在命名空间范围内提供匹配声明,无论是在之前还是之后类定义.可以通过ADL找到这样的名称,ADL同时考虑名称空间和类.在决定名称是否与先前声明的名称冲突时,此类朋友声明仅考虑最内层的封闭名称空间.

void h(int);
namespace A {
  class X {
    friend void f(X); // A::f is a friend
    class Y {
        friend void g(); // A::g is a friend
        friend void h(int); // A::h is a friend, no conflict with ::h
    };
  };
  // A::f, A::g and A::h are not visible at namespace scope
  // even though they are members of the namespace A
  X x;
  void g() {  // definition of A::g
     f(x); // A::X::f is found through ADL
  }
  void f(X) {}       // definition of A::f
  void h(int) {}     // definition of A::h
  // A::f, A::g and A::h are now visible at namespace scope
  // and they are also friends of A::X and A::X::Y
}
Run Code Online (Sandbox Code Playgroud)

这不是标准,但一般来说都是正确的.所以铿锵似乎是对的.