Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Most vexing parse: why doesn't `g( ( f() ) );` call `f`'s default constructor and pass the result to `g`'s ctor that takes a `f`?

This isn't a duplicate of Most vexing parse: why doesn't A a(()); work?, which is based on a parse in the form of A a(());, whose OP thought would be able to default-construct an A object using the extra set of parentheses.

In contrast, my question is about 2 classes, f and g, where f has a default constructor and g's ctor takes an f. I want to call g's ctor with a temporary f argument, all without using uniform initialization syntax. There's a std::cout statement in g's ctor, so the lack of output signifies a function declaration instead of g object instantiation. I annotated the sample code with 3 numbers in the comments. #1 and #2 were compiled with #3 commented out, and vice versa:

#include <iostream>
struct f {};
struct g {
    g(f) { std::cout << "g's ctor\n"; }
};
int main() {
                          // -----Output-----
    g( f() );             // #1: function declaration; expected
    g( ( f() ) );         // #2: also a function declaration; UNEXPECTED
    // g myG( ( f() ) );  // #3: "g's ctor" ONLY if #1 & #2 are commented out;
                          //     ^ ... linker error otherwise
}

#1: I thought #1 declares an anonymous function that returns a g and takes a pointer to a function that takes 0 arguments and returns an f. Am I wrong?

#2: So, I thought the extra set of parentheses in #2 will force the enclosed contents to be evaluated as a function call, namely a call to f's default ctor. But it's still a function declaration. Why?

#3: is a variant of #2, where the difference is #3's added instance name myG. #3 instantiates the object if #1 and #2 are commented out. Otherwise I get these errors in VC12:

error LNK2019: unresolved external symbol "struct g __cdecl f(void)" (?f@@YA?AUg@@XZ) referenced in function _main and

fatal error LNK1120: 1 unresolved externals.

and this error in g++ 4.8: undefined reference to 'f()'

What do they mean, and why am I getting them?

Why is #3 an object instantiation only when the instance is named?

How can I get the desired instantiation effect without naming the instance or using uniform initialization?

like image 942
CodeBricks Avatar asked Mar 22 '23 06:03

CodeBricks


1 Answers

The first one declares a function called f that takes no parameters and returns g.

     g( f() );
  //  ^     ^  redundant set of parentheses

The second one is the same, with yet another redundant set of parentheses (remember that you can have as many declarations of the same functions as you wish). They are not always useless, though. You need them, say, to declare a function that returns a function pointer:

// function taking an int and returning
// a pointer to a function that takes a char
// and returns a g
g ( *f(int) )(char);
//^         ^ needed, syntax error without them

As for third:

When #1 and #2 are there, you've got a function declaration for f in mainand g myG( ( f() ) ); is parsed as a declaration of an object of type g, named myG and initialized with a result of a function call. You get a linker error because there's no definition for f.

When #1 and #2 are commented out, the type f is visible, and the disambiguation with parentheses kicks in:

g myG( ( f() ) )
//     ^     ^   these force an expression

Without that pair, you'd get another function declaration.

What you want is this:

   ( g(f()) );
// ^        ^  must be an expression as declarations can't be parenthesized

or something less Lisp-y: static_cast<g>(f());

like image 167
jrok Avatar answered Apr 25 '23 19:04

jrok