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

Here is a program that compiles without warning, for example. GNU C ++:

$ g++ -ot -Wall -pedantic -Wshadow t.cpp $ ./t.exe Calling barney::barney() Calling foo::operator()() Calling barney::barney() 

But it does not fully 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 else, when it compiles, the result is not what I would expect. Can someone shed some light on what would be the standard behavior for this code ?

There he 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; } 

He outputs this:

 Calling barney::barney() Calling foo::operator()() Calling barney::barney() 

But I would expect this:

 Calling barney::barney() Calling barney::barney() 

Why am I getting the conclusion I am making? Is this standard behavior? If so, why, which sections of the standard are relevant?

In addition to the accepted answer, David Rodriguez gave an excellent answer, which states that in the standard, I'm allowed to declare a member named fred struct fred .

+6
source share
4 answers

Because in the fred structure, you have a fred member (of type foo ) that obscures the definition of struct fred . When you do this:

 return fred(); 

... fred refers to an object of type foo , and not to a structure type of fred , and therefore the operator foo () is called.

Note that the name "fred" refers to two different objects: a member of type foo and a type of structure fred . The compiler must choose one or the other, and it does this in accordance with the rules defined in section 3.4 ("Searching for a Name") of the C ++ standard.

You can force fred to refer to a type using the namespace qualification:

 return ::fred(); 
+6
source

It should raise an error in the question. Does not meet the standard:

9.2 [class.mem] / 13

If T is a class name, then each of the following should have a name other than T:

  • each static data member of class T;
  • each member function of class T [Note: this restriction does not apply to constructors that do not have names (12.1)];
  • each member of the class T, which in itself is a type;
  • each enumerator of each member of the class T, which is an enumerated type; and
  • each member of each anonymous association that is a member of class T.

9.2 [class.mem] / 13a

In addition, if class T has a constructor declared by user (12.1), each non-static data member of class T must have a name other than T.

As for why it finds a member and not a variable, this is pretty much consistent with how C ++ identifiers are handled, where there are two identifier spaces, one for custom types and the other for the rest of the elements (including typedefs):

 struct test {}; void test() {} // or (but not both) // int test; 

With these two definitions, test refers to a function (or variable) and struct test to a user-defined type. This is a specific corner case when using typedef when declaring a struct in C makes a difference in C ++, as it will enter a name into the common identifier space:

 typedef struct test {} test; // now "test" is in both spaces // void test() {} // error: redefinition of "test" 
+6
source

When you call f.memfunc , the call to fred() will be resolved for a member named fred type foo . Declaring memfunc as a returned struct fred will not help anything, since you cannot perform polymorphism on returned values โ€‹โ€‹in C ++.

In order for the fred() call to resolve to the struct fred constructor, qualify the call with a namespace ( ::fred() ).

+3
source

As others pointed out, you call operator() on the fred member. Either change the member name, or use ::fred() to refer to struct fred in the global scope.

+1
source

Source: https://habr.com/ru/post/886944/


All Articles