我遇到了一些这样的代码:
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);
}
此程序使用VC11 beta编译并运行,没有错误或带有/W4的警告。
明显的意图是调用B::init来重新初始化B的A基础子对象。我相信它实际上解析为名为
i
类型的名为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) {
^
可以使用冗余的类限定符来引用该类型,这似乎很奇怪。
同样,VS11和clang/gcc对
A::A(i)
的解析似乎有所不同。如果我执行A::A(b)
clang和gcc,则使用默认构造函数创建b
类型的变量A
。 VS11错误指出b
是未知标识符。 VS11似乎使用构造函数A::A(i)
(以A
作为参数)将A::A(int)
解析为临时i
的创建。消除了多余的限定符后,VS会像clang和gcc一样将源解析为变量声明,并且会产生类似于隐藏变量i
的错误。这种解析上的差异解释了为什么VS11会阻塞多个单独的限定符。
A::A::A::A(i)
,以及为什么,鉴于clang和gcc可以接受一个额外的限定词,任何多于一个额外的限定符的结果都与一个额外的限定符相同。这是在不同上下文中使用冗余限定符的另一个示例。所有编译器似乎都将其解析为一个临时构造:
class Foo {};
void bar(Foo const &) {}
int main() {
bar(Foo::Foo());
}
class D : B { using B::B; };
),但是VS似乎允许在任何地方使用它。 VS是错误的吗?clang和gcc在解析冗余限定符时是否正确? B b(A::A(i));
解析为最令人讨厌的解析时,情况甚至可能变得更糟,但是VS认为这是使用初始化程序声明了b
类型的B
变量。如此严重的差异还有很多吗? 最佳答案
尽管这种现象很可能归因于类名注入(inject),如ephemient的回答所述,但对于此特定示例,它早已被C++语言禁止使用。
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#147
需要组合A::A
来引用类构造函数,而不是类注入(inject)名称。符合要求的编译器应将A::A(i)
解释为涉及构造函数名称的非法(因此无意义)表达式。举个例子,Comeau编译器将因此而拒绝编译您的代码。
显然,VC11继续将A::A
视为对注入(inject)的类名的引用。有趣的是,我在VS2005中没有观察到此问题。
早在A::A
被解释为引用注入(inject)的名称的那一天,就可以将A
对象声明为
A::A::A::A::A::A a;
依此类推,使用任意数量的
A
。但是现在不行了。令人惊讶的是,ideone使用的GCC版本(4.3.4?)仍然遭受此问题的困扰http://ideone.com/OkR0F
您可以在您的VC11版本中尝试一下,看看是否允许这样做。
关于c++ - 为什么允许冗余的类名限定符?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11423380/