为什么允许使用冗余类名限定符?

bam*_*s53 10 c++ language-lawyer

我遇到了一些像这样的代码:

struct A {
    A() {}
    A(int) {}
};

struct B : A {
    void init(int i);
};

void B::init(int i) {
    A::A(i); // what is this?
}

int main() {
    B b;
    b.init(2);
}
Run Code Online (Sandbox Code Playgroud)

使用VC11 beta进行编译和运行,没有错误或警告/ W4.

明显的意图是调用B :: init重新初始化B的A基础子对象.我相信它实际上解析为一个名为itype 的新变量的变量声明A.使用clang进行编译会产生诊断:

ConsoleApplication1.cpp:11:14: warning: declaration shadows a local variable
        A::A(i);
             ^
ConsoleApplication1.cpp:10:22: note: previous declaration is here
    void B::init(int i) {
                     ^
ConsoleApplication1.cpp:11:14: error: redefinition of 'i' with a different type
        A::A(i);
             ^
ConsoleApplication1.cpp:10:22: note: previous definition is here
    void B::init(int i) {
                     ^
Run Code Online (Sandbox Code Playgroud)

好奇的是,类型可以用冗余类限定来引用.

此外,A::A(i)VS11和clang/gcc似乎对其进行了不同的解析.如果我做A::A(b)clang和gcc 使用默认构造函数创建一个b类型的变量A.VS11错误的说法b是一个未知的标识符.VS11似乎解析A::A(i)作为临时的创建A使用构造A::A(int)i作为参数.当消除冗余限定符时,VS将源解析为类似clang和gcc do的变量声明,并产生关于隐藏变量的类似错误i.

解析的这种差异解释了为什么VS11会超过一个额外的限定符; A::A::A::A(i)以及为什么,鉴于clang和gcc可以接受一个额外的限定符,任何多于一个额外的数字都会产生与一个额外数字相同的结果.

这是另一个在不同上下文中使用冗余限定符的示例.所有编译器似乎都将其解析为临时构造:

class Foo {};

void bar(Foo const &) {}

int main() {
    bar(Foo::Foo());
}
Run Code Online (Sandbox Code Playgroud)
  1. 为什么允许冗余限定符?
  2. 有一些上下文可以引用构造函数,例如继承构造函数(class D : B { using B::B; };)的语法,但VS似乎允许它在任何地方.VS是错误的并且在如何解析冗余限定符时是否正确且是gcc?
  3. 我知道VS在标准合规方面仍然落后一点,但我确实发现有点令人惊讶的是,现代的,积极开发的编译器可能会如此不同,在这种情况下,将冗余限定符解析为构造函数的名称(即使构造函数没有名称)与简单地将冗余限定符解析为类型,导致VS构造一个临时的,其他人声明一个变量.在B b(A::A(i));clang和gcc作为最令人烦恼的解析解析的地方可能会更糟糕,但VS认为它是用初始化程序声明一个b类型的变量B.这种严重的差异还有很多吗?
  4. 显然,在可移植代码中应避免使用冗余限定符.有没有一种好方法可以防止使用这种结构?

AnT*_*AnT 7

虽然这种现象很可能归因于类名注入,如ephemient的回答所指出的,对于这个具体的例子,它已经被C++语言禁止了很久以前.

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#147

组合A::A需要引用类构造函数,而不是类注入名称.在A::A(i)假定由一个兼容的编译器为涉及构造名的非法的(并且因此没有意义)的表达来解释.例如,Comeau编译器将因此拒绝编译代码.

显然VC11继续将其A::A视为对注入的类名的引用.有趣的是,我在VS2005中没有观察到这个问题.

A::A被解释为引用注入名称的那一天,可以将A对象声明为

A::A::A::A::A::A a;
Run Code Online (Sandbox Code Playgroud)

等等,任意数量的As.但不是了.令人惊讶的是,ideone使用的GCC版本(4.3.4?)仍然存在这个问题

http://ideone.com/OkR0F

您可以尝试使用您的VC11版本,看看它是否允许.