The problem originates from a series of constrains:
- value semantics requires that to compile a "call" you need to know parameters and return types sizes.
- that knowledge must be available to the compiler during compilation of a same translation unit but...
- knowledge coming from definitions is available only at linking time that is a separate step that happens aside of compilation.
To complicate more, C++ grammar change meaning depending if symbols are variable or types (so, without this knowledge, a<b>c
it's even impossible to know what it means: an expression involving three variable or the declaration of a variable of type given by a template instance).
A function definition can reside in another source file and the compiler -when compiling the first one- has no way to access it and hence to know about how wide should be the space to leave on the stack for parameter and return passing.
Your sample -in a wide project- will be code as
//test.h
double test();
__
//test.cpp
#include "test.h" //required to check decl & def consistence
double test()
{ /*...*/ }
__
//main.cpp
#include "test.h" // required to know about test() parameters and return
int main()
{
double z = test(); //how does "=" translate? we need to know what test returns
}
This "project" will be compiled using independent steps:
g++ -c main.cpp //on a program developer machine
g++ -c test.cpp //on a library developer machine
g++ main.o test.o -o yourprogram //on a "package distributor" machine
None of these steps can collect a "global knowledge" about all "global symbols" and their "translation" at the same time. That's why we need headers to broadcast declarations to anyone have to use them
(Note how member functions don't have this problem, being all required to stay into a same class brackets, and as a consequence are granted to fit a same translation unit)
Languages like Java or python don't have this problem, since the modules (not matter how written and loaded) are all loaded-up by a same language-machine instance, that -being unique- can if fact collect all symbols and related types.
language like D (that are like C++ in the sense of "separate compilation") allow order independence between things residing in a same module, but requires module "import" for things coming from other modules, and -in fact- do a two step translation by first collecting symbols and types and then doing call translations.
You can see another description of this problem here