8
namespace O
{
    class A{};


    class A;   // ok
    typedef A K; // ok

    struct A;  // ok(C++11): A is a class but for references and pointer it have the same meaning
    class K;   // (1) error: K is a typedef (of a class...)
}

namespace U
{
    typedef O::A A;


    class A;      // (2) error: A is a typedef (of  a class...)
}

What is the reason(s) standard C++ don't allow these cases (1 & 2) to compile?

Klaim
  • 67,274
  • 36
  • 133
  • 188
  • 1
    @SethCarnegie Why? A typedef is another name for a type, all types being either basic or class/struct why can't I forward any type, even using another name for it? – Klaim Jan 21 '13 at 17:47
  • 1
    you said it, a typedef is another name for a _type_. It would be like saying `class int`. The syntax of `class ...` requires that there not be a conflicting name. – Seth Carnegie Jan 21 '13 at 17:48
  • the typedef is made at the time it is encountered, so if you want another name for a type, put the typedef after the class. – Seth Carnegie Jan 21 '13 at 17:50
  • So if typedef act like a forward declaration, why can't I do another forward declaration? Or you're saying it's a definition, in which case the question is the same. – Klaim Jan 21 '13 at 17:51
  • @SethCarnegie Ok but then it's order dependant and if I have one header with the typedef (and an include to a big file) and another header with the forward declare (for the typedef to be used in function interfaces) it means each time I want to include both these headers, I have to do it in a specific order. Is it correct? – Klaim Jan 21 '13 at 17:53
  • A typedef does _not_ act like a forward declaration. Any types used by a typedef must already exist. – Seth Carnegie Jan 21 '13 at 17:53
  • @SethCarnegie Really? But the answer to this question says otherwise http://stackoverflow.com/questions/804894/forward-declaration-of-a-typedef-in-c and this test confirms it: http://ideone.com/WarWrQ – Klaim Jan 21 '13 at 17:58
  • That question and your test confirm what I said. Names used in typedefs must already exist. – Seth Carnegie Jan 21 '13 at 17:58
  • @SethCarnegie Ok you meant names, but it's still not the problem here. The typedefs themselves works, it's forward declaring the typedef name that don't. This is what I don't understand because until I have the typedef I can still forward declare as much as I want. – Klaim Jan 21 '13 at 18:03
  • Why do you need this? Maybe the reason the standards committee didn't put it in C++ is that no one needs it. – Seth Carnegie Jan 21 '13 at 18:05
  • @SethCarnegie Basically, I have two headers, one doing the typedef (renaming a type from another library), another forward declaring (just for using the name in the interface). The problem occurs, as you pointed correctly, when the forward declaration appears in the compilation unit after the typedef instead of before. So it means sometimes I will be forced to change the include order because of that. I bet it will be fixed with modules... – Klaim Jan 21 '13 at 19:07
  • Why not include the header that forward declares the type in the header that defines the typedef? – Seth Carnegie Jan 21 '13 at 19:16
  • @SethCarnegie That's what I'm doing now, but it don't solve the problem in cpp files including several headers with some including the forward declaration. Then they still have to be ordered. – Klaim Jan 21 '13 at 19:52
  • 1
    No, they don't; a header that uses names in some other header _should `#include` that header_, and if you do that, then the order in which the .cpp files `#include` them won't make a difference. – Seth Carnegie Jan 21 '13 at 19:57
  • You may be confusing what makes sense with what the rules say. typedef was defined by C 40 years ago and C++ has maintained compatibility. The early C compilers treated typedef like a storage class (static, extern, register) and not a new type. – brian beuning Jan 22 '13 at 01:04
  • 2
    "Maybe the reason the standards committee didn't put it in C++ is that no one needs it"... At least two person (Klaim and me) need it :) I often want to use reference (maybe const) to `std::string` in my class declaration, and I have to include instead of fwd declaring it (increasing compilation time). I do not think, that it's unneed feature to fwd declare typedefs – borisbn Jan 22 '13 at 07:18
  • It won't be fixed with modules, a typedef name will still be a different type of name from a class name, and you will need to distinguish them. – Jonathan Wakely Jan 22 '13 at 15:11
  • Yes but then there wouldn't be any header inclusion order problem that I have. – Klaim Jan 22 '13 at 16:55
  • @SethCarnegie: Forward declaring after the definition or even after another forward declaration is legal. Consider having a header `A.h` in which you need to use a reference to a class `B` that is defined in header `B.h`, and that you forward declare `class B;` in `A.h`. Suddenly, you create a file `C.cpp`, which uses both `A.h` and `B.h`, but includes `B.h` before `A.h`. If forward declaration of a class would be illegal after it has been defined, that would lead to a compilation error in this case, since that is essentially what happens in `C.cpp`. – HelloGoodbye Jun 26 '14 at 16:10

3 Answers3

13

You are confused or your example doesn't show what you're asking about. In your example code you are not trying to "forward declare a typedef" (such as thing isn't possible or useful, see below) you are trying to redeclare an existing typedef-name (i.e. an alias for one type) as a completely different type.

You've already said K is a typedef for A, then you say it's a class K. Make your mind up. It's can't be both class A and class K. Both (1) and (2) fail for that same reason.

Going through these lines of the example:

class A;   // ok
typedef A K; // ok

Right so far.

struct A;  // ok(C++11): A is a class but for references and pointer it have the same meaning

I don't know why you've said "C++11" here, this is OK in C++03 too. Classes and structs are the same kind of thing in C++. They are both "object types" and both "class types". For a forward declaration the class-key (i.e. struct or class) is interchangeable.

class K;   // (1) error: K is a typedef (of a class...)

K has been declared as a typedef for class A, the name can't be reused for declaring a new type in the same scope.

[Aside: C does allow the following, because struct names and typedef names are in separate namespaces:

struct A { };
typedef struct A K;  // N.B. need "struct A" not just "A"
struct K { }; 

But now there are two different types called struct K and K, which are unrelated. Doing this would be confusing and pretty dumb.]

But from your comments maybe that's not what you're actually trying to do anyway.

Based on your comments maybe your broken examples are misleading and what you really want to do is:

typedef class A K;   // forward declare A as class and declare K as typedef for it

This declares a typedef, for a type which is not defined yet.

It would be useless to forward-declare a typedef, you couldn't do anything with it because you wouldn't know what kind of type it was a typedef for, and there is very little you can do in C++ without knowing something about a type. Without knowing if it's an object type, reference type or function type all you can realistically do is declare another typedef for it!

Consider:

typedef K;   // declares K to be a typedef

K* f();      // function returning pointer to K
void g(K*);  // function taking pointer to K

I think you're saying you want that to be valid, so do you expect this to work?

K* k = f();
g(k);

That should work, right? You don't need to know the type of K because you only pass around pointers to it, right? Wrong. What if you later define K like this:

typedef int& K;

Now f has the signature int&* f() which is invalid. You have to know what a typedef is a typedef for, so its declaration has to say what it is not just forward-declare it as a name.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • 6
    "It would be useless to forward-declare a typedef, you couldn't do anything with it because you wouldn't know what kind of type it was a typedef for, and there is very little you can do in C++ without knowing something about a type." - then why there is not "very little you can do" with forward declaration of classes. If you can forward declare a class, why you cannot forward declare a typedef? In both cases the reason is to avoid including the type's header, when the name is mentioned in another header (as a function parameter) You want only to tell the compiler, that this type(def) exists. – Nuclear Oct 19 '16 at 08:17
  • 1
    @Nuclear, forward declaring a class tells the compiler it's a class, that tells the compiler a lot more than "it's a type", which makes it usable in contexts where you don't need to know the class details (e.g. a pointer to a class type might have a different representation to a pointer to a function type or a pointer to a scalar type). Also, class definitions can be large and costly to include, a typedef is not. It's a single statement. You don't need to include a header for a typedef, you can just redeclare it. – Jonathan Wakely Oct 19 '16 at 12:07
  • 2
    'Redeclare' is the problem we're trying to avoid. It isn't "typedef A B", it's "typedef (big hairy mess) B", and you don't want to repeat that big hairy mess. – Alan Baljeu Sep 17 '18 at 18:52
  • @AlanBaljeu what kind of mess? A class? Then you don't need to repeat it. Why do you need a typedef instead of just giving the class a name and using that name? – Jonathan Wakely Sep 17 '18 at 23:06
  • 1
    A multifaceted template instantiation, for example. – Alan Baljeu Sep 18 '18 at 00:56
4

Let me just tackle the first error:

  1. You create class A: at this point, the name 'A' is assigned to that class; no other entities in this scope can be called 'A'.
  2. Then you typedef so that K now refers to A, so no other entities in this scope can be called 'K'.
  3. Then you try to forward declare K. Forward declaration on its own is fine, but the name K is already taken by your typedef. It has little to do with A at this point. You wouldn't be able to forward declare A either, both names are already taken by your previous uses.

What are you trying to do anyways?

Sepand
  • 166
  • 1
  • I don't understand what is wrong with forward declaring a type after it's definition or declaration or typedef? – Klaim Jan 21 '13 at 19:09
  • You already have the type defined by declaring it, why would you want to (forward) declare it again? Forward declarations are for when you don't have the full declaration available, but want to tell the compiler that the class exists. If you already have the declaration, there's no need to forward declare. – Sepand Jan 21 '13 at 19:24
  • I know but I don't know the order of including of the headers containing the forward declaration, so even if I would write them in order most of the time, I easily hit cases where including multiple headers forces me to re-order them because I can't forward declare typedefs. – Klaim Jan 21 '13 at 19:53
  • Well, you should make sure not to have forward declarations (or the include that has them) after your actual declarations (or the include that has them). And your include guards should usually take care of that anyways. – Sepand Jan 21 '13 at 19:56
  • 1
    The concept of a forward declared typedef _makes no sense_. A typedef is a _definition_, it defines that symbol `K` is a synonym for symbol `A`, there's no version of that is a declaration and not a definition. – Jack Aidley Jan 21 '13 at 21:52
  • @Sepand: Forward declaring after the definition or even after another forward declaration is legal. Consider having a header `A.h` in which you need to use a reference to a class `B` that is defined in header `B.h`, and that you forward declare `class B;` in `A.h`. Suddenly, you create a file `C.cpp`, which uses both `A.h` and `B.h`, but includes `B.h` before `A.h`. If forward declaration of a class would be illegal after it has been defined, that would lead to a compilation error in this case, since that is essentially what happens in `C.cpp`. – HelloGoodbye Jun 26 '14 at 16:08
0

I can not found this thing in C++2003 standard. But C compiler doesn't allow such thing because typedef construction define new type, and then you try to define new type one more time via class A.

In principle usage of "forward declaration class" was allowed for two use cases:

  1. typedef-names
  2. pointer to the structure

This both operations doesn't need info about sizeof type and it's memory layout. Also in list above there is no "references" because there is no reference s in C language.

See also: (Samuel P. Harbison, Guy L.Steele] C A Reference, page 151.

Konstantin Burlachenko
  • 5,233
  • 2
  • 41
  • 40