Stroustrup states in C++ Language book that order of definitions in the class does not matter.
Indeed:
class C1 {
int foo() { return bar(); } // where is bar() ?
int bar() { return m_count; } // oh, here is bar(). but where is m_count ?
int m_count; // here is m_count. Better late than never !
}
This compiles. Despite misordering. As promised.
So far, so good.
However, this does not compile:
class C2 {
void baz(Inner *p) {} // we were promised that order does not matter
// is Inner defined ?
struct Inner {}; // yes, Inner is define here.
};
This looks a contradiction to Stroustrup's promise of free ordering inside the class. Quoting Stroustrup: "A member function declared within a class can refer to every member of the class as if the class were completely defined before the member function bodies were considered".
Does anybody know a ref to standard clause that allows C1 and disallows C2 ? I am curious why C2 was disallowed while C1 is allowed. Is it compiler bug that contradicts the standard, maybe ?
Note that the following compiles fine (in VS2008):
class C2 {
void baz() { Inner* i = new Inner(); }
struct Inner {};
};
There are two differences between your two examples. The first uses an undeclared symbol inside the body of a function and the second uses an undeclared type in the signature of the function.
I suspect that my example and your first example both work because the function bodies aren't resolved until after the entire class declaration has been parsed. The function signatures have to make sense as soon as they are encountered.
An identifier that denotes a type must be declared prior to use. This has to do with the complicated structure of C++ grammar. The compiler simply cannot parse certain code if it does not know beforehand which identifiers are types.
You are confusing "declarations" with "definitions". The inner class must be declared - if only forward declared - before it can be used.
The order of definition does not matter, but the order of declaration does.
In C++, a declaration means (roughly) stating the "kind" or type of a symbol:
class A; --> A is of kind "class"
void foo(int); --> foo is a function that takes an int and returns nothing
A definition, however, fully defines what the symbol relates to. As in C, a definition happens to be a declaration too.
Finally, to muddy the waters further, for user-declared types, sometimes a definition is required (complete-type) while sometimes a simple declaration is sufficient...
Now, let us revise your issue, I'll illustrate it with a simple example using C++0x:
struct A {
void foo() { ++count; }
void bar(decltype(count) c); // error: 'count' was not declared in this scope
int count;
};
struct B {
void foo() { Inner a; a.foo(); }
void bar(Inner& i); // error: 'Inner' has not been declared
struct Inner { void foo(); };
};
As you can notice, A::foo is parsed correctly, while A::bar is not.
The issue comes from the fact that the compiler "cheats": the methods body are fully analyzed only once the class has been completely parsed. Therefore it is fine to refer to a yet undeclared type/attribute/function in a function body, but it is not fine to refer to it in a function signature (declaration).
You could say that to the compiler, the code is equivalent to:
struct A {
void foo();
void bar(decltype(count) c); // error: 'count' was not declared in this scope
int count;
};
void A::foo() { ++count; }
struct B {
void foo();
void bar(Inner& i); // error: 'Inner' has not been declared
struct Inner { void foo(); };
};
void B::foo() { Inner a; a.foo(); }
Stroustrup sentence is "easy", but not necessarily accurate. For example, using Inner as an attribute requires that it is fully defined (only complete types may be used as non static attributes), which can cause further grief.
struct C {
struct Inner;
Inner foo; // error: field 'foo' has incomplete type
struct Inner { };
};
Though one could argue that Inner foo is a declaration and thus not covered by Stroustrup's quote.
I think the promise of free ordering inside the class is held if you split declaration and definition (into a header and an implementation file). Once you split them, the order really doesn't matter anymore.
Related
I have been programming in C++ for quite some time and I never thought about this until today.
Consider the following code:
struct foo
{
// compiles fine
void bar()
{
a = 1;
my_int_type b;
b = 5;
}
// Just a declaration, this fails to compile, which leads me to assume that
// even though the bar() method is declared and defined all at once, the
// compiler looks/checks-syntax-of the class interface first, and then compiles
// the respective definitions...?
void bar2(my_int_type); // COMPILE ERROR
my_int_type b; // COMPILE ERROR because it comes before the typedef declaration
typedef int my_int_type;
my_int_type a;
void bar3(my_int_type); // compiles fine
};
int main()
{
foo a;
a.bar();
return 0;
}
Is my understanding of why the errors occur (see bar2() comment above) correct/incorrect? Either way, I would appreciate an answer with a simplistic overview of how a single-pass C++ compiler would compile the code given above.
For the most part, a C++ file is parsed top-to-bottom, so entities must be declared before they are used.
In your class, bar2 and b are invalid because they both make use of my_int_type, which has not yet been declared.
One exception to the "top-to-bottom" parsing rule is member functions that are defined inside the definition of their class. When such a member function definition is parsed, it is parsed as if it appeared after the definition of the class. This is why your usage of my_int_type in bar is valid.
Effectively, this:
struct foo
{
void bar()
{
my_int_type b;
}
typedef int my_int_type;
};
is the same as:
struct foo
{
void bar();
typedef int my_int_type;
};
inline void foo::bar()
{
my_int_type b;
}
Compiler just starts to go down in a block. Any symbol which is not familiar to it will be considered as a new symbol which is not defined. This is the scheme behind the function definition or header files.
You can suppose that the compiler first makes a list of definitions so the bar() method should get compiled correctly because the definitions have provided before.
It has a lot to do with visibility. I think your confusion may come from assuming a single pass. Think of class parsing as done in two stages.
Parse class definition.
Parse method implementation.
The advantage to this is we have visibility of the entire class from within class member functions.
In C++, why does the following compile:
struct test {
void foo() { this->bar++; }
int bar;
};
But not the following:
void foo() { bar++; }
int bar;
To be clear, I do understand that in the second case foo lacks a forward declaration of bar and does not compile. I however do not understand why the first one does not fail for the same reason.
In case of a structure, scope of the member variable is available within the structure. So if you define a function within a structure that variable will be available within the function.
But that is the not the case in second scenario.
Another newbie question:
int foo(); // outer foo function
int main() {
int foo(); // inner foo function
cout << foo() << endl;
}
int foo() { // one definition
return 42;
}
From my understanding, an inner declaration of either function or object will hide outer one, if any.
So the above outer foo() and inner foo() should be two distinct functions.
But they are sharing one definition, which seems confusing.
Is it legal that two distinct functions share one definition? How about two distinct object variables?
(This is C++ question but the syntax seems also fits C.)
Edit:
It is verified that outer and inner foo are the same funciton using pointer to function:
pf_outer = 0x400792
pf_inner = 0x400792
The inner foo is just another forward deceleration of the same foo(). Consider the following example:
int foo();
int foo();
int main() {
cout << foo() << endl;
}
int foo() { // one definition
return 42;
}
This will compile and run and there is no ambiguity because the compiler will replace the use of the same function with the same code.
It is fine to re declare functions.
This is perfectly fine to redeclare a function like this, we can see this from draft C++ standard in two places, in section 3.1 Declarations and definitions which says:
A declaration (Clause 7) may introduce one or more names into a
translation unit or redeclare names introduced by previous
declarations.[...]
and goes on to say:
A declaration is a definition unless it declares a function without
specifying the function’s body [...]
and in section 13.1 Overloadable declarations paragraph 3 which says:
Parameter declarations that differ only in the use of equivalent
typedef “types” are equivalent. A typedef is not a separate type, but
only a synonym for another type (7.1.3). [ Example:
typedef int Int;
void f(int i);
void f(Int i); // OK: redeclaration of f(int)
void f(int i) { /* ... */ }
void f(Int i) { /* ... */ } // error: redefinition of f(int)
—end example ]
Both declarations will refer to the same definition, you are not allowed to redefine the function.
The function declarations of are also allowed to differ by their outermost cv-qualifiers as well.
I have two classes which both need to have links to objects of one another. Here's some example code to show my issue:
//object1.h
class object1{
object2 *pointer;
}
My other class:
//object2.h
class object2{
object1 *pointer;
}
I'm not exactly sure how I'm supposed include the two classes simultaneously in each other's file. Having an include for the other class in both files is causing me issues. Preferably, I would like to just have both the objects in one file, since their headers are only a few lines of code each, but this causes an error on whichever class is first declared in the file, since the other class header isn't preceeding it; It gives me an invalid type error.
Use a forward declaration:
//object1.h
class object2;
class object1{
object2 *pointer;
};
//object2.h
class object1;
class object2{
object1 *pointer;
};
A forward declaration introduces an incomplete type.
In short, it tells the compiler that the definition exists somewhere else.
You have some limitations with incomplete types, though. Since the compiler doesn't know its full definition, it cannot do things like allocating enough space for it, invoking member functions, checking the virtual table, etc.
Fundamentally, what you can do is to just declare references/pointers to them and pass'em around:
// Forward declaration
class X;
class Y {
private:
// X x; // Error: compiler can't figure out its size
X &x; // A reference is ok
public:
Y(X &x) : x(x) { }
// void foo() { x.foo(); } // Error: compiler can't invoke X's member
void bar();
};
// Full declaration
class X {
public:
void foo() { }
};
void Y::bar()
{
x.foo(); // Now it is OK to invoke X's member
}
A good pattern is to use forward declarations to break dependency cycles between headers or class definitions and make you wonder whatever your design could be improved.
I have been programming in C++ for quite some time and I never thought about this until today.
Consider the following code:
struct foo
{
// compiles fine
void bar()
{
a = 1;
my_int_type b;
b = 5;
}
// Just a declaration, this fails to compile, which leads me to assume that
// even though the bar() method is declared and defined all at once, the
// compiler looks/checks-syntax-of the class interface first, and then compiles
// the respective definitions...?
void bar2(my_int_type); // COMPILE ERROR
my_int_type b; // COMPILE ERROR because it comes before the typedef declaration
typedef int my_int_type;
my_int_type a;
void bar3(my_int_type); // compiles fine
};
int main()
{
foo a;
a.bar();
return 0;
}
Is my understanding of why the errors occur (see bar2() comment above) correct/incorrect? Either way, I would appreciate an answer with a simplistic overview of how a single-pass C++ compiler would compile the code given above.
For the most part, a C++ file is parsed top-to-bottom, so entities must be declared before they are used.
In your class, bar2 and b are invalid because they both make use of my_int_type, which has not yet been declared.
One exception to the "top-to-bottom" parsing rule is member functions that are defined inside the definition of their class. When such a member function definition is parsed, it is parsed as if it appeared after the definition of the class. This is why your usage of my_int_type in bar is valid.
Effectively, this:
struct foo
{
void bar()
{
my_int_type b;
}
typedef int my_int_type;
};
is the same as:
struct foo
{
void bar();
typedef int my_int_type;
};
inline void foo::bar()
{
my_int_type b;
}
Compiler just starts to go down in a block. Any symbol which is not familiar to it will be considered as a new symbol which is not defined. This is the scheme behind the function definition or header files.
You can suppose that the compiler first makes a list of definitions so the bar() method should get compiled correctly because the definitions have provided before.
It has a lot to do with visibility. I think your confusion may come from assuming a single pass. Think of class parsing as done in two stages.
Parse class definition.
Parse method implementation.
The advantage to this is we have visibility of the entire class from within class member functions.