【问题标题】:clang bug? namespaced template class' friend铿锵虫?命名空间模板类的朋友
【发布时间】:2015-05-23 22:41:42
【问题描述】:

以下代码在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++ 标准。哪个编译器是正确的?

【问题讨论】:

  • 旁注:如果你符合::bar 的条件,比如template&lt;typename U&gt; friend class ::bar;,那么clang 会编译它。所以它似乎与命名空间外的朋友可见性有关。看着一些 SO 问题,clang++ 似乎是正确的,尽管我(还)没有找到一个骗子。

标签: c++ g++ language-lawyer clang++


【解决方案1】:

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

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

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

如果 friend 声明中的名称既不是限定的也不是 template-id 并且 声明是一个函数或一个详细类型说明符,查找以确定实体是否具有 之前已声明不应考虑最内层封闭命名空间之外的任何范围

也就是这个声明:

template<typename U> friend class bar;

不会在namespace NS 之外寻找bar,因此它不会找到您之前的声明。因此,它将类模板NS::bar&lt;typename &gt; 声明为foofriend。您必须限定名称 bar 才能找到它:

template<typename U> friend class ::bar;

这似乎与GCC Bug 37804有关。

【讨论】:

    【解决方案2】:

    来自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
    }
    

    这不是标准,但总体上是正确的。所以 clang 似乎是对的。

    【讨论】:

      【解决方案3】:

      修改代码

      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 符合标准并且代码不应该编译。

      【讨论】:

        猜你喜欢
        • 2018-10-20
        • 2018-06-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多