Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why did C++ never allow functions to be used before they're declared? [closed]

OK, I know this looks like a duplicate of Why do functions need to be declared before they are used? but it doesn't seem like existing answers fully address all the details.

I know that C++ was originally designed in the 80's so it could be translated in a single pass, because computers were slow. OK. But the most recent standard was published in 2011, so I don't see why C++ compilers can't do things now that require multiple passes. It would still hurt performance, yes, but only if it actually became necessary. So the following would still only require a single pass:

void foo();
int main() { foo(); }
void foo() {}

whereas for the following, the compiler could make two (and be slower), because it doesn't know whether foo is a function or a type until it sees the declaration below:

int main() { foo(); }
void foo() {}

and if you tried to use a function without declaring it first, and the declaration is not in the current translation unit at all, then it would be an error. But if it's in the same translation unit then the compiler could just make additional passes.

My colleague argues that such a feature would save a lot of developer time, and would avoid issues with the declaration and definition not being matched. And I'm sure this has been proposed many times over and rejected every time. What is the actual reasoning behind rejecting it, i.e., the committee's rationale?

like image 485
Brian Bi Avatar asked Jul 24 '14 05:07

Brian Bi


2 Answers

Template parsing

Consider the following line of code:

a < b , c > d;

How would you parse this? There are actually two ways, depending on what a, b, c and d are. Firstly, a variable declaration

a<b,c>   d;
^^^^^^   ^
 Type   Var

in the case that a is a known template type, b and c are other known types. Secondly,

   a<b   ,   c<d ;
   ^^^       ^^^ 
boolean expressions

in the case that a, b, c and d are all variables of some sort.

The vexing parse

Or here another one:

a b(c); // is 'b' a function or a variable?

This could be a function declaration (a function with return type a and argument type c) or a variable definition (whose type is a and whose constructor argument is c).

Conclusion

There's a lot of stuff like that, unfortunately. I'm not sure, if it would be impossible to write a compiler that can deal with that kind of stuff, but it would at least be very hard to write one. Compilation times are a serious issue in C++ already. This would only make it worse. Also: It is good practice to only use what you have defined or declared already, even in other languages.

Constraints of the committee

Even if it would be reasonably possible to implement this feature, it would kill backwards compatibility. Function overloading resolution only takes prior declarations into account and the interpretation of function calls may change depending on the place a function call is written. That's the way C++ is put together. Now, the C++ standards committee is big on back-wards compatibility. In particular: They do not want to break any existing code (and rightly so). Your proposal would surely break existing code which is a no-go for the language designers.

like image 130
Ralph Tandetzky Avatar answered Oct 02 '22 20:10

Ralph Tandetzky


The current answer is because it would be unparseable.

Consider two-phase name lookup for templates, and in particular the need for typename. In templates, type-dependent names may not have been declared yet. To be able to parse them, we absolutely need typename. Without that, parsing would grind to a halt and we can't reliably proceed, so we couldn't even provide the type needed to fix the parsing problem. It's a chicken and egg problem: If we need to have parsed line 10 to parse line 5, line 10 will never be parsed because we break at line 5. typename helps us get past line 5 so we can learn the actual type on line 10.

Here, we'd have a similar problem. Consider this code under your assumptions:

struct Foo { };
int bar () { return Foo(); }
int Foo () { return 42; }

To parse this code, we need to know whether Foo denotes a type or function.

like image 22
MSalters Avatar answered Oct 02 '22 20:10

MSalters