Reputation: 1278
For many times now, I have had problems with the declaration and definition order in C++:
struct A {
void Test() { B(); }
};
void B() {
A a;
}
Of course this can be solved by predeclaring B()
. Usually this is good enough to solve any of these problems. But when working with module based header-only libraries or similarily complex include systems, this declaration/definition concept can be really painful. I have included a simple example below.
Nowadays most modern language compilers do a two-pass over the source files to build the declarations in the first pass and process the definitions in the second one. Introducing this scheme into C++ shouldn't break any old code either. Therefore,
This is an example of a module based header library, which has blocking includes because of missing predeclarations. To solve this, the user of the library would have to predeclare the "missing" classes, which is not feasible. Of course this problem might be solved by using a common include header that orders all declarations before definitions, but with a two-pass this code would also work, no modification required.
oom.h
#pragma once
#include "string.h"
struct OOM {
String message;
};
string.h
#pragma once
#include "array.h"
struct String {
Array data;
};
array.h
#pragma once
struct Array {
void Alloc();
};
#include "oom.h"
void Array::Alloc() { throw OOM(); }
str_usage.cpp
#include "string.h"
int main() {
String str;
}
Upvotes: 0
Views: 172
Reputation: 137325
void f(int);
void g() { f(3.14); }
void f(double);
g
currently calls f(int)
, because it's the only f
visible. What does it call in your world?
f(double)
, you just broke copious existing code.If you came up with some rules to make it still call f(int)
, then that means if I write
void g2() { f2(3.14); }
void f2(double);
and then introduce a worse match for the argument - say, void f2(int);
before g2
, g2
will suddenly start calling the wrong thing. That's a maintainability nightmare.
Upvotes: 2
Reputation: 76360
A much simpler solution is to separate class definitions from function definitions:
struct A {
void Test();
};
struct B {
A a;
};
inline void A::Test() {
B();
}
Upvotes: 1
Reputation: 110148
There are ambiguities in the C++ grammar that can only be resolved if you know what an identifier refers to.
For example:
a * b;
can be either a multiplication if a
is a variable, or a pointer declaration if a
is a type. Each of these leads to a different parse tree, so the parser must know what a
is.
This means that parsing and name resolution cannot be performed in separate passes, but must be done in one pass, leading to the requirement to pre-declare names.
Upvotes: 0