I came across some code like this:
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);
}
This compiled and ran using VC11 beta with no errors or warnings with /W4.
The apparent intent is for calling B::init to reinitialize the B's A base subobject. I believe it actually parses as a variable declaration for a new variable named i
with type A
. Compiling with clang produces diagnostics:
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) {
^
It seems curious that the type can be referred to with the redundant class qualification.
Also, A::A(i)
appears to be parsed differently by VS11 and clang/gcc. If I do A::A(b)
clang and gcc create a variable b
of type A
using the default constructor. VS11 errors out on that saying b
is an unknown identifier. VS11 appears to parse A::A(i)
as the creation of a temporary A
using the constructor A::A(int)
with i
as the parameter. When the redundant qualifier is eliminated VS parses the source as a variable declaration like clang and gcc do, and produces a similar error about shadowing the variable i
.
This difference in parsing explains why VS11 will choke on more than a single extra qualifier; A::A::A::A(i)
, and why, given that clang and gcc can accept one extra qualifier, any number more than one extra has the same result as one extra.
Here's another example with the redundant qualifiers in a different context. All compiler seem to parse this as a temporary construction:
class Foo {};
void bar(Foo const &) {}
int main() {
bar(Foo::Foo());
}
- Why are redundant qualifiers allowed at all?
- There are some contexts where constructors can be referred to, such as the syntax for inheriting constructors (
class D : B { using B::B; };
) but VS seems to be allowing it anywhere. Is VS wrong and are clang and gcc right in how redundant qualifiers are parsed?
- I know VS is still a fair bit behind in terms of standards compliance, but I do find it a bit surprising that modern, actively developed compilers could be so divergent, in this case resolving a redundant qualifier as the name of a constructor (even though constructors don't have names) vs. resolving redundant qualifiers simply to the type, resulting in VS constructing a temporary where the others declare a variable. It can be made even worse where
B b(A::A(i));
is parsed by clang and gcc as the most vexing parse, but VS sees it as declaring a variable b
of type B
with an initializer. Are there still many differences this severe?
- Clearly, redundant qualifiers should be avoided in portable code. Is there a good way to prevent this construct from being used?
See Question&Answers more detail:
os 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…