我遇到了一些像这样的代码:
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) {
^
似乎很奇怪可以使用冗余类限定来引用类型。
此外,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());
}
class D : B { using B::B; };
)的语法,但VS似乎允许它在任何地方。 VS是错误的,并且在如何解析冗余限定符时是否正确且是gcc?B b(A::A(i));
作为最令人烦恼的解析时,可能会更糟糕,但VS认为它是使用初始化程序声明类型为b
的变量B
。这种严重的差异还有很多吗?答案 0 :(得分: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;
等等,具有任意数量的A
s。但不是了。令人惊讶的是,ideone使用的GCC版本(4.3.4?)仍然存在这个问题
您可以尝试使用您的VC11版本,看看是否允许这样做。
答案 1 :(得分:3)
来自ISO / IEC 14882:2011最终草案§9,
² class-name 被插入到在看到 class-name 之后立即声明它的范围内。 class-name 也会插入到类本身的范围内,这称为 inject-class-name 。
写作时
A::A(i);
它与声明
相同A i;
因为额外的括号是多余的(您可以添加任意数量的括号),A::A
指的是A
。
来自§14.6.1,
¹与普通(非模板)类一样,类模板具有注入类名(第9条)。 inject-class-name可以用作模板名称或类型名称。当它与 template-argument-list 一起使用时,作为模板 template-parameter 的 template-argument ,或作为最终标识符在友元类模板声明的 elaborated-type-specifier 中,它引用了类模板本身。否则,它等同于 template-name ,后跟
<>
中包含的类模板的 template-parameters 。
注入的类名似乎是一种方便,允许A<...>
在类中简称为A
。