6

The title comes from the famous site C++ FAQ by Marshall Cline.

The author claims that there is a difference between the following two code examples.

Suppose that List is the name of some class. Then function f() declares a local List object called x:

void f()
{
    List x;     // Local object named x (of class List)
    ...
}

But function g() declares a function called x() that returns a List:

void g()
{
    List x();   // Function named x (that returns a List)
    ...
}

But is it really wrong to use the second variant?

And if it really is a declaration wouldn't the compiler complain that you cannot declare a function within a function?

Xeo
  • 129,499
  • 52
  • 291
  • 397
maba
  • 47,113
  • 10
  • 108
  • 118
  • 1
    The "ability" to *declare* (not define) functions inside of other functions stems from C, were you may have wanted to do this at one point. In C++, it only serves to give you the most vexing parse. :s – Xeo Sep 06 '12 at 09:33
  • clang++ does produce a warning: empty parentheses interpreted as a function declaration [-Wvexing-parse] – kennytm Sep 06 '12 at 09:35
  • 1
    Visual Studio similarly: - warning C4930: 'blah blah(void)': prototyped function not called (was a variable definition intended?). – Andy Krouwel Nov 09 '15 at 15:29

1 Answers1

8

And if it really is a declaration wouldn't the compiler complain that you cannot declare a function within a function.

Of course not. Because you can declare a function withing a function.

This is called most vexing parse and it's well documented. In fact, it would be an error on behalf of the compiler to treat

List x();

as a variable declaration.

But is it really wrong to use the second variant?

If you want a variable, then yes. If you want to declare a function... kinda yes. You can, but usually you'd do it outside of a function scope.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 5
    This is not really the most vexing parse (only "half" of that), the most vexing parse is `List x(List());`. – kennytm Sep 06 '12 at 09:34
  • But since `List x(1);` is a correct statement one would think that the corresponding way to create an object with a zero-argument constructor would be `List x();`. – maba Sep 06 '12 at 09:44
  • 2
    @Kenny: It's the second-most-vexing parse, then. Or the third-most-vexing parse, and yours is the second, because `vector v(istream_reader(cin), istream_reader());` is *really* freaking vexing ;-) – Steve Jessop Sep 06 '12 at 09:44
  • @maba I see why one might think that, but no... :) – Luchian Grigore Sep 06 '12 at 09:45
  • @maba: one would think that, and one would be incorrect. Which is why it's "vexing". Because C++ wasn't designed from scratch, it absorbed a bunch of C stuff that sometimes gets in the way. – Steve Jessop Sep 06 '12 at 09:45
  • 1
    @Luchian: Does `List x{};` work in C++11? I can't remember the new-fangled syntax. If so it's worth a mention. – Steve Jessop Sep 06 '12 at 09:49
  • A better way would have been to let `List x(void);` be the function declaration and `List x();` be the object creation. Just saying :-) – maba Sep 06 '12 at 09:50
  • @Steve: Yes, that's the syntax. – Xeo Sep 06 '12 at 09:50
  • 1
    @maba: but then a C function that does `int some_function_name();` to declare a function inside another function, wouldn't compile as C++. Because it would instead define an `int` called `some_function_name` with initial value 0. But you're right, now that it's 2012 we can say it would be better if C++ didn't bother with C compatibility. Too late :-( – Steve Jessop Sep 06 '12 at 10:01
  • @SteveJessop There were a lot of languages designed at the time which didn't bother with C++ compatibility. C++ compatibility is one of the reasons why we now use C++ instead of one of them. – James Kanze Sep 06 '12 at 10:07
  • @James: Sure, I know why it was a good idea at the time (80s) and for the target audience it had when Stroustrup started. By 1998, people were successfully inventing popular languages that did not have C compatibility (Java, Perl, etc), but I guess even then, C++ was right to target C programmers as its audience. Probably in 2012, C++ doesn't live primarily by converting C programmers, but now it has to pay for backward compatibility with itself, never mind C. – Steve Jessop Sep 06 '12 at 10:11
  • And anyway, the last time someone took some C code and compiled it as C++ with every confidence that it would compile and do the same thing as it did in C, is probably about 1983 or something. So personally, I think that C compatibility in small details like this isn't necessary, given that C++ is not C compatible in respect of certain other details of similar smallness. But that's very much a case-by-case judgement call what's "important" to support. I'm not criticising C++, just slightly regretting it. – Steve Jessop Sep 06 '12 at 10:20
  • @SteveJessop - it's not about converting C programmers; it's about **interoperability** between C and C++, and that's still an important goal. For example, C++11's atomics were designed to work with C11's atomics, so there's no problem mixing C++ and C code that share atomic variables. – Pete Becker Sep 06 '12 at 11:01
  • @Pete: I don't see how the ability to declare a function inside another function, by means of what in C++ is the most vexing parse, has any bearing at all on interoperability. The motivation for it, surely, was to allow C code to compile as C++ with the same meaning. So I agree that in 2012, the motivation is not converting C programmers. In 1983, it was, because it was a selling point that you could take your existing C code, call it C++, and start using C++ features. It's a decision made around 1979-1983 (and not changed prior to standardisation in 1998) that we're now paying for. – Steve Jessop Sep 06 '12 at 11:06
  • Past tense versus present tense; yesterday's goals versus today's goals. – Pete Becker Sep 06 '12 at 11:09
  • @Pete: indeed, that's what I've been saying, it's my explanation for why a design decision that now looks poor, was locked in at a time when it looked wise (or at least practical). Apologies, I thought you thought I was claiming that today's goal is source-compatibility with C, and that you were correcting me. I agree that it isn't, I didn't intend to claim that, and probably you never thought I had anyway :-) – Steve Jessop Sep 06 '12 at 11:14
  • @SteveJessop Removing the possibility of declaring a function inside another function does _not_ solve the most vexing parse problem. The most vexing parse can occur anywhere you can define a variable (including at namespace scope). Using the new unified initialization format might solve the problem (but I'll probably be retired before I can use C++11 professionally). – James Kanze Sep 06 '12 at 11:21
  • @James: fair enough, inside functions is the just the place that it most commonly arises in confused SO questions. The cause is retaining C's declaration syntax while adding C++'s direct-initialization syntax, creating an ambiguity that has to be decided. So I suppose there's another way that C++ could have avoided the vexing parse -- instead of breaking C compatibility, it could have used some other (less convenient) syntax for direct initialization. My aside about "smallness" is wrong: breaking C compatibility for function declarations would make bilingual header files a PITA, so that's big. – Steve Jessop Sep 06 '12 at 11:25
  • @SteveJessop That's what they (finally) did. Better late than never. – James Kanze Sep 06 '12 at 11:29
  • @James: Heh, true. But I reckon *I'll* probably be retired before not only can I use C++11 professionally, but everyone is consistently using `{}` for initialization and nobody gets vexed. – Steve Jessop Sep 06 '12 at 11:30