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
.
When new is used to allocate memory for a C++ class object, the object's constructor is called after the memory is allocated. Use the delete operator to deallocate the memory allocated by the new operator.
Yes, it is possible to call special member functions explicitly by the programmer.
Because in the fred
structure you have a member fred
(of type foo
) which shadows the definition of struct fred
. When you then do:
return fred();
... the fred
refers to the object of type foo
rather than the fred
struct type, and so the foo
() operator is called.
Notice that the name "fred" refers to two different things - the member, of type foo, and the fred
struct 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 fred
to refer to the type using a namespace qualification:
return ::fred();
On the it should generate an error part of the question. Not according to the standard:
9.2 [class.mem]/13
If T is the name of a class, then each of the following shall have a name different from T:
9.2 [class.mem]/13a
In addition, if class T has a user-declared constructor (12.1), every nonstatic data member of class T shall have a name different from T.
As to why does it find the member rather than the variable, that is pretty much consistent with how the identifiers are handled in C++, where there are two identifier spaces, one for user defined types and another for the rest of the elements (including typedefs):
struct test {};
void test() {}
// or (but not both)
// int test;
With those two definitions, test
refers to the function (or variable), and struct test
to the user defined type. This is the particular corner case where the use of typedef
when declaring an struct
in C makes a difference in C++, as it will inject the name in the common identifier space:
typedef struct test {} test; // now "test" is in both spaces
// void test() {} // error: redefinition of "test"
When you call f.memfunc
, the call to fred()
will resolve to the member named fred
of type foo
. Declaring memfunc
as returning struct fred
won't help you anything, since you cannot do polymorphism on return values in C++.
To make the fred()
call resolve to the constructor of struct fred
, qualify the call with a namespace (::fred()
).
As others have pointed out, you are calling the operator()
on the member fred
. Either change the name of the member, or use ::fred()
to refer to the struct fred
in the global scope.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With