Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this program call operator () instead of the constructor?

Tags:

c++

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.

like image 637
Omnifarious Avatar asked Apr 29 '11 08:04

Omnifarious


People also ask

Does operator new call constructor?

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.

Can you manually call a constructor?

Yes, it is possible to call special member functions explicitly by the programmer.


4 Answers

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();
like image 141
davmac Avatar answered Oct 17 '22 23:10

davmac


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:

  • every static data member of class T;
  • every member function of class T [Note: this restriction does not apply to constructors, which do not have names (12.1) ] ;
  • every member of class T that is itself a type;
  • every enumerator of every member of class T that is an enumerated type; and
  • every member of every anonymous union that is a member of class 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"
like image 35
David Rodríguez - dribeas Avatar answered Oct 17 '22 22:10

David Rodríguez - dribeas


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()).

like image 3
Håvard S Avatar answered Oct 17 '22 23:10

Håvard S


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.

like image 1
hammar Avatar answered Oct 17 '22 22:10

hammar