C++ 创建一个未定义的类作为friend,并在以后定义它

C++ 创建一个未定义的类作为friend,并在以后定义它,c++,templates,declaration,local,friend,C++,Templates,Declaration,Local,Friend,结交不认识的朋友 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 tha

结交不认识的朋友

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;
}
模板
班级名单
{
受保护的:
甲级{
int x;
int-y;
私人:

friend class b;//代码格式不正确,Comeau拒绝该代码,并给出以下错误

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

我认为这是G++中的一个bug。英特尔C++也拒绝它。你可以通过定义类<代码> B<代码>上面的代码来修复代码。

template <typename U > class b { 
        int z;
        U y;
};
class a {
        int x;
        int y;
    private:
        friend class b<T>;  
};
模板类b{
intz;
尤伊;
};
甲级{
int x;
int-y;
私人:
b级朋友;
};

//运行此命令-它现在将为您编译

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;

}

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

类列表
{
公众:
甲级{
typedef int类型;
b班的朋友;//没关系!
};
模板b类;
};
b类{
List::a::键入一个_int;//允许访问私有成员
};
标准规定为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关于其范围

类中声明的名称的潜在作用域不仅包括以下声明性区域 名称的声明符,以及所有函数体、默认参数和构造函数的声明符- 该类中的初始值设定项(包括嵌套类中的此类内容)

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

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

友元类或函数可以在类模板中声明 它的朋友的名字被视为专门化在实例化时被显式声明

因此,以下代码无效:

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; 
模板
班级名单
{
公众:
甲级{
typedef int类型;
b班的朋友;//没关系!
};
模板b类;
};
//POI
列表::a x;

当这导致隐式实例化
List::a
时,将在“//POI”中查找名称
a
好像有一个明确的专门化声明。在这种情况下,模板
列表::b
已经声明了,这个查找将命中它并发出一个错误,因为它是一个模板类而不是非模板类。

谢谢。但在您的帖子中有趣的是,在其他编译器上,这是一个重新声明错误.据我所知,重新声明不是一个错误,它不应该说不同类型的声明有冲突吗?@user420536:不一定。在MSVC++上,我得到
List::b]:非类模板已经声明为类模板。
(重新声明错误)。允许不同的编译器以不同的方式解析代码,从而允许发出不同的错误消息。我没有发现该错误消息有任何错误。
:)
您的分析似乎是正确的,只是
在命名空间中提供匹配声明之前,通过简单的名称查找无法找到朋友的姓名作用域(在授予友谊的类声明之前或之后)。你的代码没有在COMMO、英特尔C++和MSVC++中编译。@ Prason。你引用的文本在这个例子中没有意义。朋友的名字没有在这里被查找(模板被查找)。.Compiler没有定义标准,所以我不反对编译器接受或拒绝它。GCC和Clang接受它是件好事,但我的答案并不取决于它们,而仅仅取决于我引用的标准文本。EDG前端(Intel和Comeau使用的)MSVC真的有比这更多的bug,所以这并不让我感到惊讶)。我发现文本有点难以解析。我可能错过了一些重要的东西。别介意。还要看一看和一大堆答案。无论如何,如果我的分析不正确,或者如果因为标准被打破而无法给出正确的分析,我希望被展示一个标准的文本和参数是这样显示的:)@Johannes:+1,我喜欢Comeau xD
:)
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;