C++ 在另一个类的构造函数的成员初始值设定项中声明的类是否在其外部可见?

C++ 在另一个类的构造函数的成员初始值设定项中声明的类是否在其外部可见?,c++,language-lawyer,C++,Language Lawyer,考虑以下代码: struct Foo { void* p; Foo() : p{(class Bar*)0} {} }; Bar* bar; GCC(8.2)和Clang(7.0.0)的最新版本无法编译它。ICC(19.0.1)也是如此。 然而,MSVC(v19.16)编译得很干净 GCC中的错误是:error:'Bar'未命名类型;你是说“char”吗? Clang和ICC发布了类似的信息 一致性查看器 那么,根据标准,哪种编译器是正确的 [basic.lookup.

考虑以下代码:

struct Foo { 
    void* p; 
    Foo() : p{(class Bar*)0} {}
};

Bar* bar;
GCC(8.2)和Clang(7.0.0)的最新版本无法编译它。ICC(19.0.1)也是如此。
然而,MSVC(v19.16)编译得很干净

GCC中的错误是:
error:'Bar'未命名类型;你是说“char”吗?

Clang和ICC发布了类似的信息

一致性查看器

那么,根据标准,哪种编译器是正确的

  • [basic.lookup.elab]。。。如果详细的类型说明符是由 类键和此查找未找到以前声明的类型名称。。。详细说明的类型说明符是一个声明,它引入了[basic.scope.pdecl]中描述的类名

  • [basic.scope.pdecl]-用于表单的详细类型说明符

    类密钥标识符

    如果在decl说明符seq或 命名空间作用域中定义的函数[因作用域而不适用]。。。否则,除非作为友元声明,否则将在 包含声明的最小命名空间或块范围

现在,棘手的一点。成员初始值设定项列表是否“包含”在构造函数的范围内?如果不是,则最小的块或名称空间范围是全局名称空间,并且程序的格式良好。如果是,则最小的作用域是构造函数的块作用域,因此该类不会在全局作用域中声明

就我所知,没有规则说meminitlist“包含在构造函数的块范围内”。正是在花括号之外界定了范围。因此,该计划的形式良好


Mem init list是构造函数主体[dcl.fct.def.general]的一部分,但该主体既不是块范围也不是命名空间范围,因此它与[basic.scope.pdecl]中的规则无关。

这是关于声明规则和范围规则的;(类型说明符也可以在表达式中声明类名)

: 由声明声明声明的名称将引入中的范围 声明中提到的,除了朋友在场 说明符,详细类型说明符的某些用法 ([dcl.type.elab]),并使用指令([namespace.udir])更改此设置 一般行为

构造函数有一个作用域(如前所述),类似于其中声明的任何内容。这就是为什么这应该是有效的

struct Foo { 
    void* p; 
    Foo() : p{(class Bar*)0} {
        Bar* bar;
    }
};

但不是:

struct Foo { 
    void* p; 
    Foo() : p{(class Bar*)0} {
        //Scope of Bar only exists here
    }
};

Bar* bar;
编辑: 有关这方面的更多详细信息:

<>所谓C++中的“正向声明”,在技术上是多种“形式”之一;表格可能会引入一个名称。(强调矿山)

类的声明点,在 详细的类型说明符如下所示:

  • (7.1)表格声明


    类键属性说明符seqopt标识符
    /
    你的意思是“char”吗?
    :gcc听起来像是21世纪初的谷歌:我相信MSVC是正确的,理查德·史密斯似乎也同意。(来自,最令人惊讶的部分:“GCC、Clang和ICC都认为这是“错误的”,但MSVC遵循规范。)@P.W我的理解与eerorika的基本相同:1。标识符是“在包含声明的最小命名空间或块范围中声明的。”2。由于块作用域是复合语句()的产物,并且mem初始值设定项列表出现在任何复合语句之外,“最小的命名空间或块作用域”只能是全局命名空间作用域。我真的想知道为什么这被投票选为“不清楚的问题语句”…@T.E.D.那么您使用了错误的语言;)我不明白“name”和“evaluate”是如何混合的:查找名称和计算表达式,对吗?@YSC求值和声明对我来说似乎是正交的,但我包含了该规则,因为它是我能找到的关于成员init列表中作用域的唯一规则(除了关于从何处查找名称的规则之外)。如果我假设这个规则不适用,那我就错了。虽然我正在努力,但我认为我们找不到更好的匹配规则。这可能是一个措辞问题。顺便说一句,我认为你对这个问题本身缺乏答案:它合法吗?@eerorika:你答案的最后一段也总结了我的困境。但是说
    class Bar
    应该含蓄地声明它,不是吗?即使有?@LightnessRacesinOrbit,如果我没有弄错的话,所谓的“前进宣言”实际上是多种形式的声明之一;而且只是。我不确定我是否正确地理解了你的评论……不,你理解正确,而且(似乎)回答正确-谢谢():你会考虑这个答案吗?我认为这是关键)@ LightnessRacesinOrbit,谢谢你的建议,做了…把答案改进了一点:-)
    void foo(){
        void* n = (class Boo*)(0);
        Boo* b;
    }
    
    class [[deprecated("WTF")]] Mew;
    
    void foo(){
        void* n = (class [[deprecated("WTF")]] Boo*)(0);
    }
    
    class [[deprecated("WTF")]] Mew;
    
    attributes ignored on elaborated-type-specifier that is not a forward declaration [-Wattributes]
    
    struct Foo { 
        static void foo(void* = (class Bat*)0); //Leaks it past class-scope
    };
    
    void moo(){
        Bat* m;
    }
    
    struct Foo { 
        class Moo{
            class Mew{ 
                void foo(void* = (class Bat*)0); //
            };
        };
    };
    
    void moo(){
        Bat* m;
    }
    
    namespace zoo {
        struct Foo { 
            class Moo{
                class Mew{ 
                    void foo(void* = (class Bat*)0);
                };
            };
        };
    }
    void moo(){
        zoo::Bat* m;
    }
    
    struct Foo { 
        void* p; 
        Foo(void* = (class Zoo*)(0)) 
            : p{(class Bar*)0} {
            Bar* bar;
        }
    };
    
    Zoo* m;     //Zoo* leaked
    //Bar* n    //Bar* did not leak