3

While trying to understand the "Most vexing parse" problem in C/C++, this question immediately springs to mind - why have a syntax that causes this problem to begin with?

For example,

class Timer
{
public:
    Timer();
};

class TimeKeeper
{
public:
    TimeKeeper(const Timer& t);

    int get_time()
    {
        return 1;
    }
};

int main()
{
    TimeKeeper time_keeper(Timer());
    // the above is eq to this: TimeKeeper time_keeper(Timer (*)());
}

So why not simply disallow TimeKeeper time_keeper(Timer()) to be a function declaration that takes an unnamed function ptr returning type Timer? Is TimeKeeper time_keeper(Timer (*)()) inferior as a function declarator?

Is it not due to this syntax that we even get this ambiguity or am I missing something?

EDIT: Personally, I've never used TimeKeeper time_keeper(Timer()) as a function declaration. I've always used the Timer (*)() to specify a function pointer as I find it clearer.

Zach Saw
  • 4,308
  • 3
  • 33
  • 49
  • 1
    I think that C compatibility here is the problem, they probably didn't want to break compatibility with the rules for interpretation of declarations. – Matteo Italia Jul 18 '12 at 06:25
  • I would think that's not much of an issue since syntax like `MyFunction()` function declaration with no return type has been deprecated and GCC forbids it with a compiler error these days. That would've broken a lot of code at one stage or another. – Zach Saw Jul 18 '12 at 06:28
  • That's what C++ did to solve the problem. They just chose the other interpretation to be the one to disallow. – David Schwartz Jul 18 '12 at 06:36
  • @DavidSchwartz Choosing the other interpretation seems counter intuitive to me, hence the question. Also, why even allow function declaration in the middle of a function anyway? 15 years of C++ coding and I've never done that. – Zach Saw Jul 18 '12 at 06:40
  • 1
    When you look at one specific problem that arose out of a complicated series of decisions, it's easy to pick apart the individual decisions. Hindsight is the only exact science. Each of these decisions makes sense individually. – David Schwartz Jul 18 '12 at 06:55
  • @DavidSchwartz Could you answer and state the necessary changes you think is the correct decisions with the benefit of hindsight that we have now? – Zach Saw Jul 18 '12 at 07:03
  • 1
    @ZachSaw: No, because it's almost impossible to correctly judge the consequences of those decisions to really know they are correct. I could just as easily have made the same decisions that C++ actually made and then would have been surprised when the "most vexing parse" emerged. I'm just as likely to create a new "most vexing parse". – David Schwartz Jul 18 '12 at 07:06
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/14040/discussion-between-zach-saw-and-david-schwartz) – Zach Saw Jul 18 '12 at 07:24
  • History and backwards compatibility are the reasons. – James Kanze Jul 18 '12 at 07:49

2 Answers2

8

So why not simply disallow TimeKeeper time_keeper(Timer()) to be a function declaration that takes an unnamed function ptr returning type Timer?

Suppose for a while that this function declaration is diallowed, because it uses unnamed parameter. If that is so, then the following declarations will be disallowed as well:

int max(int,int);  //error (in hypothetical C++)
int min(int,int);  //error (in hypothetical C++)

And then the programmers will be forced to write the parameter name, in the declarations as well:

int max(int a,int b);  //ok 
int min(int a,int b);  //ok 

But then someone else would stand up and ask : "Why am I forced to write the parameter name(s) in the declarations when it doesn't use it? Why is it not optional?"

I think this guy is rational and what he asked has point. It is indeed irrational to force programmers to name the parameter in the declarations.

--

Reading your comment, it seems that you think the following declarations are exactly same:

int max(Timer());
int max(Timer(*)());

No. They're not exactly same from the syntax point of view, though they are exactly same from the behavior point of view.

The subtle difference is that in the former, the parameter type is a function which takes nothing, and returns Timer, while in the later, the parameter type is a pointer to a function which takes nothing, and returns Timer. Do you see the difference?

But then the question is, why are they same behavior-wise? Well the answer is, in the former declaration, the parameter type is adjusted and then becomes a pointer type, and so it behaves the same as the second declaration.

The C++03 Standard says in §13.1/3,

Parameter declarations that differ only in that one is a function type and the other is a pointer to the same function type are equivalent. That is, the function type is adjusted to become a pointer to function type (8.3.5).

I hope it is same in C++11 also.

--

Your doubt (taken from the comment):

Still not any closer to understanding why we need the 2 syntax?

Because they are two different types. Function type, and pointer to function type. Only as parameter type, they behaves same. Otherwise, they're different. See my answer here to see where they behave differently:

And since they behave differently in other situations, we have them, we need them. The Standard doesn't (and should not) disallow one syntax, just because as parameter type they behave same.

Community
  • 1
  • 1
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • This is what I don't understand: `int max(Timer)` would be fine as an unnamed param, but `int max(Timer())` shouldn't be? – Zach Saw Jul 18 '12 at 06:48
  • @ZachSaw: Why? One is fine, other is not? – Nawaz Jul 18 '12 at 06:49
  • Because in the second case, you have another unambiguous syntax `int max(Timer (*)())`? – Zach Saw Jul 18 '12 at 06:49
  • @ZachSaw: *"another"*? `int max(Timer()) ` and `int max(Timer (*)())` are *almost* same, **not** *exactly* same. In the former, the parameter type is function which takes nothing, and returns `Timer`, while in the later, the parameter type is a *pointer* to a function which takes nothing, and returns `Timer`. Do you see the difference? – Nawaz Jul 18 '12 at 06:53
  • Not really. Could you revise your answer to write up about this? I would accept it cause that's what I didn't understand and was trying to. – Zach Saw Jul 18 '12 at 06:55
  • Could you elaborate more about that? Like where you would need to use the former syntax and not the latter? – Zach Saw Jul 18 '12 at 06:59
  • If they behave exactly same, why have 2 syntax where one creates ambiguity? *I genuinely am trying to understand. – Zach Saw Jul 18 '12 at 07:02
  • @ZachSaw: I edited my answer. See the quote from the spec as well. – Nawaz Jul 18 '12 at 07:03
  • Still not any closer to understanding why we need the 2 syntax? – Zach Saw Jul 18 '12 at 07:06
  • 1
    @ZachSaw: It's not that we need them. It's that we have them. There is no rule that a language should only have what is needed or that there should only be one way to accomplish a task. – David Schwartz Jul 18 '12 at 07:07
  • @ZachSaw: Because they are two *different* types. Only in the parameter type, they behaves same. Otherwise, they're different. See my answer here for more detail: http://stackoverflow.com/questions/7321993/reference-to-function-syntax-with-and-without – Nawaz Jul 18 '12 at 07:07
  • @DavidSchwartz Even when it creates ambiguities? – Zach Saw Jul 18 '12 at 07:14
  • 1
    @ZachSaw: It's very easy to criticize the solution that was chosen because you see every problem it causes. It's easy to imagine solutions that seem better because you have no idea what problems they would cause because they don't actually get used and tested. This is basically an unsolvable problem. There are no perfect languages. – David Schwartz Jul 18 '12 at 07:16
  • @Nawaz: The `Timer()` one is equivalent to your FunctionType in your other answer I presume? – Zach Saw Jul 18 '12 at 07:16
  • @DavidSchwartz: I merely wanted to understand the language further. For me, if something is logical, it means one less thing for me to remember. – Zach Saw Jul 18 '12 at 07:18
  • 1
    Then the short answer is that any syntax that doesn't allow anything ambiguous will likely be too simple to be useful. This is especially true if the syntax has to evolve over decades and respond to changing requirements. – David Schwartz Jul 18 '12 at 07:19
  • The issue (in this particular case) isn't whether omitting the name is legal or not. The issue is the rewriting of a function call as a pointer to a function. This and the rewriting of an array as a pointer are warts left over from C, and still present for reasons of C compatibility. – James Kanze Jul 18 '12 at 07:52
  • @DavidSchwartz Not at all. Without the issue of backwards compatibility, it would be trivial to require all variable declarations to start with the keyword `var`, and all function declarations to start with the keyword `func` (for example). Which would make the code much more readable as well; it's not just the compiler which has problems parsing C++ declarations. – James Kanze Jul 18 '12 at 07:54
  • @JamesKanze: Then when you need to add something that's neither a variable nor a function, do you overload `func` or `var`? Or do you create a new keyword that might conflict with existing code? – David Schwartz Jul 18 '12 at 07:59
  • @DavidSchwartz I specifically said: "without the issue of backwards compatibility". It's clear that this is not an option for an existing language, especially one as widely used as C++. (C++11 did introduce a new syntax for functions, which is a great improvement. But it still has to support the old one, and will for quite some time.) – James Kanze Jul 18 '12 at 08:32
  • 1
    @JamesKanze: I see, by "without the issue of backwards compatability", you meant *with* a horrible issue of backwards compatibility just waiting to bite in the future. So you solve this problem but set us up for the next one to be much worse. – David Schwartz Jul 18 '12 at 08:35
  • 1
    @DavidSchwartz Anytime you introduce a new feature to an existing language, you risk problems of backwards compatibility. When C++ added `class`, it broke some of my C programs, which had variables of that name. There is, regretfully, no easy solution for this; what happens in practice is that compiler authors provide options to disable the new keyword. – James Kanze Jul 18 '12 at 08:43
  • You should be considering the option of resolvi g the ambiguity to be an object declaration with an initializer. then in cases where there are no ambiguities you are still allowed to omit a parameter name – Johannes Schaub - litb Jul 18 '12 at 09:12
  • Sorry it's been so long but just another question. To me it looks like the whole problem stems from §13.1/3. If it doesn't auto decay function type to pointer to function type, wouldn't the whole problem go away? – Zach Saw Feb 06 '14 at 03:15
3

Just a note that... you have been heard :)

C++11 specifically addressed this issue with the introduction of the uniform initialization syntax. Now you can write TimeKeeper time_keeper{Timer{}}; and there is no parse ambiguity.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • and it has a different meaning for most structs and many classes – Johannes Schaub - litb Jul 18 '12 at 09:14
  • Why did they not simply remove §13.1/3 in C++11? Wouldn't that get rid of the most vexing parse problem entirely? I'm sure there are other things that broke backward compatibility going to C++11. – Zach Saw Feb 06 '14 at 03:18
  • 1
    Or in the case of `void f(double adouble) { int i(int(adouble)); }`, the parenthesis around adouble shouldn't be superflous to begin with. Yes, again, the std says it is superflous but if they start cleaning up these ambiguities in the std, C++ would be a much less confusing language. The argument of backward compatibility will always be there, but each time a new C++ std is introduced, can we honestly say it is fully backward compatible? If it is, there's no reason for compilers to require a new switch to turn on the new features (e.g. -std=c++11). – Zach Saw Feb 06 '14 at 03:25