Here is a program that compiles without warning on e.g. GNU C++:
$ g++ -o t -Wall -pedantic -Wshadow t.cpp
$ ./t.exe
Calling barney::barney()
Calling foo::operator()()
Calling barney::barney()
But it completely fails to compile on MSVC++:
$ cl /EHsc t.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86
Copyright (C) Microsoft Corporation. All rights reserved.
t.cpp
t.cpp(17) : error C2380: type(s) preceding 'fred' (constructor with return type, or illegal redefinition of current class-name?)
t.cpp(17) : error C2208: 'fred' : no members defined using this type
What’s more, when it does compile, the output is not what I’d expect. Can someone shed some light on what would be the required standard behaviour for this code?
Here it is:
#include <iostream>
using ::std::cerr;
struct fred;
struct foo {
inline fred operator ()();
};
struct barney {
barney() : v_(0) { cerr << "Calling barney::barney()\n"; }
int v_;
};
struct fred : public barney {
foo fred;
int joe;
struct fred memfunc() { return fred(); }
};
inline fred foo::operator ()()
{
cerr << "Calling foo::operator()()\n"; return fred();
}
int main(int argc, const char *argv[])
{
fred f;
f.memfunc();
return 0;
}
It outputs this:
Calling barney::barney()
Calling foo::operator()()
Calling barney::barney()
But I would expect this:
Calling barney::barney()
Calling barney::barney()
Why do I get the output I do? Is this standard behavior? If it is, why, which sections of the standard are relevant?
In addition to the accepted answer, David Rodriguez gave an excellent answer detailing where it says in the standard that I’m allowed to declare the member named fred of struct fred.
Because in the
fredstructure you have a memberfred(of typefoo) which shadows the definition ofstruct fred. When you then do:… the
fredrefers to the object of typefoorather than thefredstruct type, and so thefoo() operator is called.Notice that the name “fred” refers to two different things – the member, of type foo, and the
fredstruct type. The compiler must choose one or the other, and it does so according to the rules defined in section 3.4 (“Name Lookup”) of the C++ standard.You can force
fredto refer to the type using a namespace qualification: