【问题标题】:Making an undefined class as friend, and defining it later将未定义的类作为朋友,稍后再定义
【发布时间】:2010-11-25 06:43:41
【问题描述】:

结交不知名的朋友

template<typename T>
class List
{
protected:

    class a {
        int x;
        int y;
    private:
        friend class b;  // <------------ Why this is not an error? 
    };

    template <typename U > class b {  //If that is not a error this should be an error
        int z;
        U y;
    };

    public:
        List() {
            a* ptr = (a *)new unsigned char[sizeof(a)];
        }
};

int main() {
    List<int>  mylist;
}

请通过此链接,我的问题是代码中的 cmets。 我正试图让另一个班级成为我班的朋友。但是那个班在交朋友的时候是不知道的。允许它的 C++ 规则是什么。 后来我以与朋友声明不兼容的方式定义了该类。为什么不抛出错误。 谢谢

【问题讨论】:

    标签: c++ templates declaration local friend


    【解决方案1】:

    是的,您的代码无效!这是一个有趣的展示模板如何以微妙的方式改变代码的含义。以下代码有效的:

    class List
    {
    public:
        class a {
            typedef int type;
            friend class b; // that's fine!
        };
    
        template <typename U > class b;
    };
    
    class b {
      List::a::type an_int; // allowed to access private member
    };
    

    标准在 7.3.1.2/3 表示

    如果非本地类中的友元声明首先声明了一个类或函数83) 友元类或函数是最内层封闭命名空间的成员。

    什么时候是“第一个声明的类”?那里也是这么说的

    当查找声明为友元的类或函数的先前声明时,并且当友元类或函数的名称既不是限定名称也不是模板 ID 时,最内层封闭命名空间范围之外的范围不是考虑。

    “b 类”的查找从 7.1.5.3/2 委托给 3.4.4,后者又委托给 3.4/7 的非限定名称查找。现在所有的问题是模板名称“b”在友元声明类 a 中是否可见。如果不是,则找不到该名称,并且友元声明将引用全局范围内的新声明类。 3.3.6/1关于它的范围说

    在类中声明的名称的潜在范围不仅包括以下声明区域 名称的声明符,还包括所有函数体、默认参数和构造函数 ctor- 该类中的初始化程序(包括嵌套类中的此类内容)。

    忽略一些会使该措辞不适用于此处的迂腐点(这是一个缺陷,但在该段落的 C++0x 版本中已修复,这也使这更易于阅读),此列表不包括朋友声明作为该模板名称可见的区域。

    然而,朋友是在类模板的成员类中声明的。当成员类被实例化不同时,查找应用 - 查找在类模板中声明的朋友名称!标准说

    Friend 类或函数可以在类模板中声明。当一个模板被实例化时, 其朋友的名称被视为在其实例化时已明确声明了特化。

    所以下面的代码是无效的:

    template<typename T>
    class List
    {
    public:
        class a {
            typedef int type;
            friend class b; // that's fine!
        };
    
        template <typename U > class b;
    };
    
    // POI
    List<int>::a x; 
    

    当这导致 List&lt;int&gt;::a 被隐式实例化时,名称 a 会在“// POI”处查找,就好像已经声明了一个显式的特化。在这种情况下,模板List::b 已经被声明,这个查找将命中它并发出错误,因为它是一个模板而不是一个非模板类。

    【讨论】:

    • 你的分析似乎是正确的,除了The name of the friend is not found by simple name lookup until a matching declaration is provided in that namespace scope (either before or after the class declaration granting friendship)。您的代码不会在 Comeau、英特尔 C++ 和 MSVC++ 中编译。
    • @Prasoon 您引用的文字在这种情况下没有任何意义。此处不查朋友姓名(查模板)。编译器没有定义标准,所以我不反对编译器接受或拒绝它。 GCC 和 Clang 接受它是一件好事,但我的回答并不取决于他们,而仅取决于我引用的标准文本。 EDG 前端(英特尔和 Comeau 使用)和 MSVC 确实有比这更多的错误,所以这并不让我感到惊讶)。
    • 我发现文本有点难以解析。我可能错过了一些重要的事情。别介意。也看看this similar question 和一堆答案。
    • 在任何情况下,如果我的分析不正确或如果由于标准被破坏而无法给出正确的分析我希望显示标准的文本和参数,显示如下:)
    • @Johannes : +1,我喜欢 Comeau xD :)
    【解决方案2】:

    //运行这个-它现在会为你编译

    template <typename U > class b; //<----- forward declaration
    
    template<typename T>
    class List
    {
    protected:
    
    
            class a {
            int x;
            int y;
            private:
              friend class b<T>;  // <------------ Add <T>
            };
            template <typename U > class b { 
              int z;
              U y;
            };
    
            public:
            List() {
              a* ptr = (a *)new unsigned char[sizeof(a)];
            }
    };
    
    int main() {
        List<int>  mylist;
    
    }
    

    【讨论】:

      【解决方案3】:

      代码格式错误,Comeau 拒绝它并给出以下错误

      error: invalid redeclaration of type name "b" (declared at
            line 11)
      

      我认为这是 g++ 中的一个错误。英特尔 C++ 也拒绝它。您可以通过在A 上方定义类B 来修复代码。

      template <typename U > class b { 
              int z;
              U y;
      };
      class a {
              int x;
              int y;
          private:
              friend class b<T>;  
      };
      

      【讨论】:

      • 谢谢。但是您帖子中有趣的是,在其他编译器上,这是一个重新声明错误。据我所知,重新声明不是错误,它不应该说不同类型的冲突声明吗?
      • @user420536 :不一定。在 MSVC++ 上,我得到 List&lt;T&gt;::b' : non-class template has already been declared as a class template.(重新声明错误)。允许不同的编译器以不同的方式解析代码,从而允许发出不同的错误消息。我没有发现该错误消息有任何问题。 :)
      猜你喜欢
      • 2012-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-07
      相关资源
      最近更新 更多