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

时间:2015-05-23 22:41:43

标签: 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;
}

失败了:

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() {}   
        ^

bar应该可以访问foo的私有构造函数,但看起来却没有。如果我删除namespace NS,则会进行编译。

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

3 个答案:

答案 0 :(得分:14)

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

  

首先在名称空间中声明的每个名称都是该名称空间的成员。如果一个friend声明   非本地类首先声明朋友是成员的类,函数,类模板或函数模板   最里面的封闭命名空间。

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

  

如果friend声明中的名称既不合格也不是 template-id ,   声明是一个函数或 elaborated-type-specifier ,查找以确定实体是否具有   之前已声明不应考虑最内层封闭命名空间之外的任何范围

即,这个声明:

template<typename U> friend class bar;

不会在bar之外查找namespace NS,因此它不会找到您之前的声明。因此,它将类模板NS::bar<typename >声明为friend foo。您必须限定名称bar才能找到它:

template<typename U> friend class ::bar;

这似乎与GCC Bug 37804有关。

答案 1 :(得分:5)

来自cppreference

  

朋友声明在非本地类X中引入的名称   成为X的最内部封闭命名空间的成员,但他们这样做   查找不可见(既不合格也不合格)   除非在命名空间范围内提供匹配的声明   在类定义之前或之后。这样的名字可以通过找到   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
}

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

答案 2 :(得分:2)

将代码更改为

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;
}

用clang编译。问题似乎是命名空间查找。 代码

template<typename U> friend class bar;

实际上声明类NS :: bar是NS :: foo的朋友,所以foo不应该受到影响。我的猜测是clang符合标准且代码不能编译。