304

I'm trying to do something like the following:

enum E;

void Foo(E e);

enum E {A, B, C};

which the compiler rejects. I've had a quick look on Google and the consensus seems to be "you can't do it", but I can't understand why. Can anyone explain?

Clarification 2: I'm doing this as I have private methods in a class that take said enum, and I do not want the enum's values exposed - so, for example, I do not want anyone to know that E is defined as

enum E {
    FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X
}

as project X is not something I want my users to know about.

So, I wanted to forward declare the enum so I could put the private methods in the header file, declare the enum internally in the cpp, and distribute the built library file and header to people.

As for the compiler - it's GCC.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
szevvy
  • 3,446
  • 2
  • 18
  • 10
  • So many years into this and somehow StackOverflow lured me back ;) As a postmortem suggestion - *just don't do this* *especially* in the scenario you describe. I would prefer to define an abstract interface and expose this tot he users and keep the enum definition and all other implementation details with the internal implementation that no one else sees on my side allowing me to do whatever whenever and have full control of when users see anything. – RnR Sep 04 '14 at 10:07
  • If you read past the accepted answer, this is completely possible since C++11. – fuzzyTew Jun 24 '20 at 20:53

19 Answers19

264

Forward declaration of enums is possible since C++11. Previously, the reason enum types couldn't be forward declared was because the size of the enumeration depended on its contents. As long as the size of the enumeration is specified by the application, it can be forward declared:

enum Enum1;                     // Illegal in C++03 and C++11; no size is explicitly specified.
enum Enum2 : unsigned int;      // Legal in C++11.
enum class Enum3;               // Legal in C++11, because enum class declarations have a default type of "int".
enum class Enum4: unsigned int; // Legal C++11.
enum Enum2 : unsigned short;    // Illegal in C++11, because Enum2 was previously declared with a different type.
prapin
  • 6,395
  • 5
  • 26
  • 44
user119017
  • 2,641
  • 1
  • 15
  • 2
  • 1
    Is there any compiler support for this feature? GCC 4.5 doesn't seem to have it :( – rubenvb Dec 09 '10 at 16:06
  • 4
    @rubenvb So does Visual C++ 11 (2012) http://blogs.msdn.com/b/vcblog/archive/2011/09/12/10209291.aspx – knatten Oct 15 '12 at 14:14
  • I was looking for enum32_t and with your answer enum XXX : uint32_t {a,b,c}; – Fantastory Feb 17 '15 at 08:19
  • I thought scoped enums (enum class) were implemented in C++11? If so, how are they then legal in C++0X? – Terrabits Jun 17 '16 at 22:14
  • What if the enum is in some class's name space? `enum class MyClass::Enum4: unsigned int;` doesn't seem to work. – A. Vieira Mar 08 '18 at 14:56
  • Apparently you [can't forward declare nested types](https://stackoverflow.com/questions/951234/forward-declaration-of-nested-types-classes-in-c). Does that mean enums should always be made global, if used by multiple classes? – A. Vieira Mar 09 '18 at 11:34
  • 2
    C++0x was the working name for C++11, @Terrabits, prior to it being officially standardised. The logic is that if a feature is known (or highly likely) to be included in an updated standard, then usage of that feature before the standard is officially released tends to use the working name. (E.g., compilers that supported C++11 features before official standardisation in 2011 had C++0x support, compilers that supported C++17 features before official standardisation had C++1z support, and compilers that support C++20 features right now (2019) have C++2a support.) – Justin Time - Reinstate Monica Aug 05 '19 at 16:50
  • You're quite welcome, @Terrabits. It can be a bit hard to grok at first, especially in the case of `0x == beta(11)`, but it helps to be aware of it since many compilers tend to use working names for the flags to enable upcoming standard features for use. – Justin Time - Reinstate Monica Aug 08 '19 at 16:56
230

The reason the enum can't be forward declared is that, without knowing the values, the compiler can't know the storage required for the enum variable. C++ compilers are allowed to specify the actual storage space based on the size necessary to contain all the values specified. If all that is visible is the forward declaration, the translation unit can't know what storage size has been chosen – it could be a char, or an int, or something else.


From Section 7.2.5 of the ISO C++ Standard:

The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration. It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0. The value of sizeof() applied to an enumeration type, an object of enumeration type, or an enumerator, is the value of sizeof() applied to the underlying type.

Since the caller to the function must know the sizes of the parameters to correctly set up the call stack, the number of enumerations in an enumeration list must be known before the function prototype.

Update:

In C++0X, a syntax for forward declaring enum types has been proposed and accepted. You can see the proposal at Forward declaration of enumerations (rev.3)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
KJAWolf
  • 2,722
  • 1
  • 15
  • 6
  • Now that your answer is accepted, Could you edit your answer? – prakash Sep 29 '08 at 15:44
  • Note that the very _intent_ of defining an enum is to restrict it's possible values. Forward declaring trumps this. – xtofl Jan 27 '09 at 13:10
  • 33
    -1. Your reasoning cannot be correct -- otherwise, why are you allowed to forward-declare "class C;" and then declare a function prototype that takes or returns a C, before fully defining C? – j_random_hacker Mar 26 '09 at 09:01
  • 2
    My point is that, when declaring a function prototype, the compiler doesn't need to know a parameter's size, regardless of whether it is a class or an enum. Clearly it's useful to be able to specify incomplete class types here, and this works fine -- so why not incomplete enum types? – j_random_hacker Mar 26 '09 at 09:04
  • If this is the case, is it possible to declare the storage size in the forward declaration. – Grant Peters Mar 26 '09 at 10:57
  • 120
    @j_random: You can not use a class before it's fully defined - you can only use a pointer or a reference to that class and that's because their sizes and ways of operations do not depend on what the class is. – RnR Mar 27 '09 at 08:11
  • 1
    @RnR: Of course you cannot declare a variable of class C, or ask for sizeof (C), before C is defined, but the fact that you *can* declare pointers and references to C before it's defined limits compilation dependencies (fewer #includes == faster). It ought to be possible to do the same with enums. – j_random_hacker Mar 29 '09 at 16:26
  • 27
    The size of a reference or pointer to a class object is set by the compiler, and independent of the actual size of the object - it is the size of pointers and references. The enum is an object, and its size is needed for the compiler to access the correct storage. – KJAWolf Apr 19 '09 at 20:41
  • 19
    Logically it would be able to declare pointers/references to enums if we had forward-declaring enums, just as we can do with classes. It's just that you do not often deal with pointers to enums :) – Pavel Minaev Aug 15 '09 at 02:31
  • 24
    I know this discussion ended up a long time ago, but I have to line up with @j_random_hacker here: the problem here is not about pointer or reference to incomplete types, but about the use of incomplete types in declarations. Since it is legal to do `struct S; void foo(S s);` (note that `foo` is only declared, not defined), then there is no reason why we couldn't do `enum E; void foo(E e);` as well. In both cases, the size is not needed. – Luc Touraille Nov 26 '10 at 10:44
  • 7
    Thanks @Luc. Maybe I was a bit gruff, it was a surprise to learn that `struct S; void foo(S s);` is legal C++, because of course `struct S; void foo(S s) {}` is not (`S` must be defined for this case). But it seems most people have not noticed the declaration/definition distinction. – j_random_hacker Nov 28 '10 at 10:27
  • 8
    I know this is an old discussion, but I want to re-emphasize that, despite the number of people voting him up, @RnR is largely wrong - it is perfectly legal to use a class before it is fully defined in certain situations, including as parameters to functions or return values from functions. If you don't believe me, try it - declare a `class A;` and a `void foo(A);`, then provide the implementations for both and see if it compiles. – Tom Jul 24 '12 at 13:14
  • 2
    @Tom - I'd argue that a declaration is not _using_ a class; whereas dereferencing or accessing fields on a class, is. +1 for RnR ;) – nevelis Feb 22 '13 at 09:36
  • 3
    @nevelis - What RnR said was, "you can only use a pointer or a reference to that class," which is simply not true, ergo, wrong. – Tom Feb 22 '13 at 11:02
  • RnR was of course correct that you can only use classes before they have been defined if the usage is such that "their sizes and ways of operations do not depend on what the class is". Arguing that "a pointer or a reference" is not the only such usage is nitpicking, and the downvotes of this answer are bogus because the logic is correct in re the OP's question. And the answer is correct that enums can be forward declared in C++11 because the underlying type, and thus the size, is known. – Jim Balter Feb 28 '13 at 04:34
  • 10
    It's not nitpicking - the whole thread of discussion is about using class forward-definitions in a function prototype. j_random_hacker said you can do it, RnR said you can't - he is wrong. – Tom May 13 '13 at 10:58
  • 1
    I don't understand the size argument that is repeatedly used. A class's size us unknown until it is fully defined, yet it is allowed to be forward declared and then used not only as a pointer and reference, but also as a parameter, return type and when passed as a parameter to another function, see Herb Sutter's http://www.gotw.ca/publications/mill04.htm inline std::ostream& operator<<( std::ostream& os, const X& x ) { return x.print(os); } example and explanation. – jla Jun 11 '14 at 22:31
  • 2
    I chose bad example code. As j_random_hacker says, you can declare a function prototype that takes or returns a class (by value even) without knowing it's size. Sure, "the caller to the function must know the sizes of the parameters to correctly setup the call stack", but the caller can include the definition of of a class to get it's size. Why is that not the case for an enum? – jla Jun 11 '14 at 22:58
  • 1
    @RnR, I don't agree with your comment. In the OP's example the argument is used as a parameter of a function declaration. If it was a forward-declared class in the parameter list, everything would just work. So compiler doesn't need to know the size of the parameter when the function is declared. – Kan Li Sep 03 '14 at 01:01
  • 2
    @JimBalter, I am on the same side as j_random_hacker, LucTouraille and Tom, that even though RnR gets a big number of vote up, it is very wrong. Ones who voted up it don't fully understand the question. – Kan Li Sep 03 '14 at 01:09
  • Wow... 5 years :) What's funny is I would also like to be able to forward declare an enum ;) As to the frustration with not being able to do so and use them in a *declaration* of something - we also can't forward declare a typedef and use it in a declaration right? (typedef A;) Is it bad and making us unproductive? I do agree my general comment was not correct IF we consider using a class in a declaration as *using* that class BUT I agree with Jim that considering it "using" is a bit of a stretch and I can confirm that's not what I meant. The real answer should have been "because you can't" ;) – RnR Sep 04 '14 at 10:00
  • 2
    **−1** Re the “since” in “Since the caller to the function must know the sizes of the parameters to correctly setup the call stack, the number of enumerations in an enumeration list must be known before the function prototype.” Nope. By value arguments as well as the function result can be of incomplete type in the prototype. In a definition or call they must be known, but not in the prototype. – Cheers and hth. - Alf Mar 13 '15 at 15:35
  • I would suggest that the detractors of @KJAWolf remove their comments. You have been publicly embarrassing yourself for 7 years now. The updated C++ standard addresses this specific issue with a remedy that confirms exactly what Wolf has stated. And thank you Wolf for posting the remedy. – natersoz Dec 07 '17 at 16:56
  • 1
    @natersoz: Embarrassing how? The standard isn't wrong; people are just saying the standard's requirement is more strict than actually necessary. And indeed it seems to be. If you believe otherwise, please explain to people *why* the size is actually required (in your own words). I suspect you won't be able to. – user541686 Jan 15 '21 at 12:36
95

You can forward-declare an enum in C++11, so long as you declare its storage type at the same time. The syntax looks like this:

enum E : short;
void foo(E e);

....

enum E : short
{
    VALUE_1,
    VALUE_2,
    ....
}

In fact, if the function never refers to the values of the enumeration, you don't need the complete declaration at all at that point.

This is supported by G++ 4.6 and onwards (-std=c++0x or -std=c++11 in more recent versions). Visual C++ 2013 supports this; in earlier versions it has some sort of non-standard support that I haven't figured out yet - I found some suggestion that a simple forward declaration is legal, but your mileage may vary.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Tom
  • 7,269
  • 1
  • 42
  • 69
  • 6
    +1 because this is the only answer that mentions you need to declare the type in your declaration as well as your definition. – turoni Apr 28 '17 at 08:39
  • I believe the partial support in early MSVC was backported from C++/CLI's `enum class` as a C++ extension (before C++11's different `enum class`), at least if I remember correctly. The compiler allowed you to specify an enum's underlying type, but didn't support `enum class` or forward-declared enums, and warned you that qualifying an enumerator with the enum's scope was a non-standard extension. I remember it working roughly the same as specifying the underlying type in C++11 does, except more annoying because you had to suppress the warning. – Justin Time - Reinstate Monica Aug 12 '19 at 05:23
  • This is what I was looking after finding out that I can use #ifndef macro to define enum only once while removing additional header to share between two sources to access one enum. This is far better solution. – Advent Aug 09 '23 at 20:37
  • 1
    @Advent note that to avoid multiple compilation of headers, it is generally better to use `#pragma once` rather than `#ifndef` include guards - it is one line instead of three and less error-prone. – Tom Aug 23 '23 at 14:13
  • @Tom yeah, I switched to #pragma once from #ifdef lately. – Advent Aug 23 '23 at 19:41
31

Forward declaring things in C++ is very useful because it dramatically speeds up compilation time. You can forward declare several things in C++ including: struct, class, function, etc...

But can you forward declare an enum in C++?

No, you can't.

But why not allow it? If it were allowed you could define your enum type in your header file, and your enum values in your source file. It sounds like it should be allowed, right?

Wrong.

In C++ there is no default type for enum like there is in C# (int). In C++ your enum type will be determined by the compiler to be any type that will fit the range of values you have for your enum.

What does that mean?

It means that your enum's underlying type cannot be fully determined until you have all of the values of the enum defined. Which means you cannot separate the declaration and definition of your enum. And therefore you cannot forward declare an enum in C++.

The ISO C++ standard S7.2.5:

The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration. It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0. The value of sizeof() applied to an enumeration type, an object of enumeration type, or an enumerator, is the value of sizeof() applied to the underlying type.

You can determine the size of an enumerated type in C++ by using the sizeof operator. The size of the enumerated type is the size of its underlying type. In this way you can guess which type your compiler is using for your enum.

What if you specify the type of your enum explicitly like this:

enum Color : char { Red=0, Green=1, Blue=2};
assert(sizeof Color == 1);

Can you then forward declare your enum?

No. But why not?

Specifying the type of an enum is not actually part of the current C++ standard. It is a VC++ extension. It will be part of C++0x though.

Source

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Brian R. Bondy
  • 339,232
  • 124
  • 596
  • 636
15

[My answer is wrong, but I've left it here because the comments are useful].

Forward declaring enums is non-standard, because pointers to different enum types are not guaranteed to be the same size. The compiler may need to see the definition to know what size pointers can be used with this type.

In practice, at least on all the popular compilers, pointers to enums are a consistent size. Forward declaration of enums is provided as a language extension by Visual C++, for example.

James Hopkin
  • 13,797
  • 1
  • 42
  • 71
  • 2
    -1. If your reasoning was correct, the same reasoning would imply that forward declarations of class types could not be used to create pointers to those types -- but they can. – j_random_hacker Mar 26 '09 at 09:25
  • 7
    +1. Reasoning is correct. The specific case is platforms where sizeof(char*) > sizeof(int*). Both can be underlying types for an enum, depending on range. Classes do not have underlying types so the analogy is false. – MSalters Mar 26 '09 at 10:30
  • @MSalters: "Underlying type" is indeed a property of an enum and not a class, but that's irrelevant -- the same thing that is done for classes *could* be done for enums without a contradiction arising. Namely, using full-size (i.e. char*) pointers, and allowing pointers to be used before definition. – j_random_hacker Mar 29 '09 at 16:14
  • 3
    @MSalters: Example: "struct S { int x; };" Now, sizeof (S*) *must* be equal to the size of any other pointer-to-struct, since C++ allows such a pointer to be declared and used prior to the definition of S... – j_random_hacker Mar 29 '09 at 16:20
  • 1
    @MSalters: ... On a platform where sizeof(char*) > sizeof(int*), using such a "full-size" pointer for this particular struct may be inefficient, but it dramatically simplifies coding -- and exactly the same thing could, and should, be done for enum types. – j_random_hacker Mar 29 '09 at 16:21
  • 4
    pointers to data and pointers to functions can be different sizes, but I'm fairly sure that data pointers have to round-trip (cast to another data pointer type, then back to the original, need to still work), which implies that all data pointers are the same size. – Ben Voigt Mar 09 '10 at 04:53
  • While I am surprised to learn sizeof(char*) > sizeof(int*) is ever possible, sizeof(char*) == sizeof(int*) on everything from 8088 to ARM to Itanium, so it's dumb to issue an error on all platforms for code that is almost always unambiguous. More importantly, even if (sizeof(char*) != sizeof(int*)) the compiler does not need to know the size of E or E* just to compile "enum E; void Foo(E e);". The only reasonable requirement is that E needs to be defined before you can call or define Foo(). – Qwertie Apr 24 '12 at 17:34
8

There is indeed no such thing as a forward declaration of enum. As an enum's definition doesn't contain any code that could depend on other code using the enum, it's usually not a problem to define the enum completely when you're first declaring it.

If the only use of your enum is by private member functions, you can implement encapsulation by having the enum itself as a private member of that class. The enum still has to be fully defined at the point of declaration, that is, within the class definition. However, this is not a bigger problem as declaring private member functions there, and is not a worse exposal of implementation internals than that.

If you need a deeper degree of concealment for your implementation details, you can break it into an abstract interface, only consisting of pure virtual functions, and a concrete, completely concealed, class implementing (inheriting) the interface. Creation of class instances can be handled by a factory or a static member function of the interface. That way, even the real class name, let alone its private functions, won't be exposed.

Alexey Feldgendler
  • 1,792
  • 9
  • 17
6

I am just noting that the reason actually is that the size of the enum is not yet known after forward declaration. Well, you use forward declaration of a struct to be able to pass a pointer around or refer to an object from a place that's referred to in the forward declared struct definition itself too.

Forward declaring an enum would not be too useful, because one would wish to be able to pass around the enum by-value. You couldn't even have a pointer to it, because I recently got told some platforms use pointers of different size for char than for int or long. So it all depends on the content of the enum.

The current C++ Standard explicitly disallows doing something like

enum X;

(in 7.1.5.3/1). But the next C++ Standard due to next year allows the following, which convinced me the problem actually has to do with the underlying type:

enum X : int;

It's known as an "opaque" enum declaration. You can even use X by value in the following code. And its enumerators can later be defined in a later redeclaration of the enumeration. See 7.2 in the current working draft.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
5

I'd do it this way:

[in the public header]

typedef unsigned long E;

void Foo(E e);

[in the internal header]

enum Econtent { FUNCTIONALITY_NORMAL, FUNCTIONALITY_RESTRICTED, FUNCTIONALITY_FOR_PROJECT_X,
  FORCE_32BIT = 0xFFFFFFFF };

By adding FORCE_32BIT we ensure that Econtent compiles to a long, so it's interchangeable with E.

Laurie Cheers
  • 1,461
  • 1
  • 9
  • 7
  • 2
    Of course, this means that (A) the types of E and Econtent differ, and (B) on LP64 systems, sizeof(E) = 2 * sizeof(EContent). Trivial fix: ULONG_MAX, easier to read as well. – MSalters Mar 26 '09 at 10:32
2

You can wrap the enum in a struct, adding in some constructors and type conversions, and forward declare the struct instead.

#define ENUM_CLASS(NAME, TYPE, VALUES...) \
struct NAME { \
    enum e { VALUES }; \
    explicit NAME(TYPE v) : val(v) {} \
    NAME(e v) : val(v) {} \
    operator e() const { return e(val); } \
    private:\
        TYPE val; \
}

This appears to work: http://ideone.com/TYtP2

2

There's some dissent since this got bumped (sort of), so here's some relevant bits from the standard. Research shows that the standard doesn't really define forward declaration, nor does it explicitly state that enums can or can't be forward declared.

First, from dcl.enum, section 7.2:

The underlying type of an enumeration is an integral type that can represent all the enumerator values defined in the enumeration. It is implementation-defined which integral type is used as the underlying type for an enumeration except that the underlying type shall not be larger than int unless the value of an enumerator cannot fit in an int or unsigned int. If the enumerator-list is empty, the underlying type is as if the enumeration had a single enumerator with value 0. The value of sizeof() applied to an enumeration type, an object of enumeration type, or an enumerator, is the value of sizeof() applied to the underlying type.

So the underlying type of an enum is implementation-defined, with one minor restriction.

Next we flip to the section on "incomplete types" (3.9), which is about as close as we come to any standard on forward declarations:

A class that has been declared but not defined, or an array of unknown size or of incomplete element type, is an incompletely-defined object type.

A class type (such as "class X") might be incomplete at one point in a translation unit and complete later on; the type "class X" is the same type at both points. The declared type of an array object might be an array of incomplete class type and therefore incomplete; if the class type is completed later on in the translation unit, the array type becomes complete; the array type at those two points is the same type. The declared type of an array object might be an array of unknown size and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points ("array of unknown bound of T" and "array of N T") are different types. The type of a pointer to array of unknown size, or of a type defined by a typedef declaration to be an array of unknown size, cannot be completed.

So there, the standard pretty much laid out the types that can be forward declared. Enum wasn't there, so compiler authors generally regard forward declaring as disallowed by the standard due to the variable size of its underlying type.

It makes sense, too. Enums are usually referenced in by-value situations, and the compiler would indeed need to know the storage size in those situations. Since the storage size is implementation defined, many compilers may just choose to use 32 bit values for the underlying type of every enum, at which point it becomes possible to forward declare them.

An interesting experiment might be to try forward declaring an enum in Visual Studio, then forcing it to use an underlying type greater than sizeof(int) as explained above to see what happens.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Dan Olson
  • 22,849
  • 4
  • 42
  • 56
  • note that it explicitly disallows "enum foo;" in 7.1.5.3/1 (but as with everything, as long as the compiler warns, it may still compile such code, of course) – Johannes Schaub - litb Mar 26 '09 at 18:49
  • Thanks for pointing it out, that's a really esoteric paragraph and it might take me a week to parse it. But it's good to know it's there. – Dan Olson Mar 27 '09 at 05:50
  • no worries.some standard paragraphs are really strange :) well,an elaborated type specifier is something where you specify a type,but also specify something more to make it unambiguous. e.g "struct X" instead of "X", or "enum Y" instead of only "Y".you need it to assert something is really a type. – Johannes Schaub - litb Mar 27 '09 at 11:31
  • so you can use it like this: "class X * foo;" if X wasn't forward declared yet. or "typename X::foo" in a template for disambiguation. or "class link obj;" if there is a function "link" in the same scope that would shadow the class having the same name. – Johannes Schaub - litb Mar 27 '09 at 11:33
  • in 3.4.4 it says they are used if some non-type name hides a type name. that's where they are most often used, apart from forward declaring like "class X;" (here it is the sole constitute of a declaration). it talks about them in non-templates here. however, 14.6/3 lists a use of them in templates. – Johannes Schaub - litb Mar 27 '09 at 12:04
2

If you really don't want your enum to appear in your header file and ensure that it is only used by private methods, then one solution can be to go with the PIMPL principle.

It's a technique that ensure to hide the class internals in the headers by just declaring:

class A
{
public:
    ...
private:
    void* pImpl;
};

Then in your implementation file (.cpp), you declare a class that will be the representation of the internals.

class AImpl
{
public:
    AImpl(A* pThis): m_pThis(pThis) {}

    ... all private methods here ...
private:
    A* m_pThis;
};

You must dynamically create the implementation in the class constructor and delete it in the destructor and when implementing public method, you must use:

((AImpl*)pImpl)->PrivateMethod();

There are pros for using PIMPL. One is that it decouples your class header from its implementation, and there isn't any need to recompile other classes when changing one class implementation. Another is that is speeds up your compilation time, because your headers are so simple.

But it's a pain to use, so you should really ask yourself if just declaring your enum as private in the header is that much a trouble.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vincent Robert
  • 35,564
  • 14
  • 82
  • 119
1

It seems it can not be forward-declared in GCC!

An interesting discussion is here.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
prakash
  • 58,901
  • 25
  • 93
  • 115
1

For VC++, here's the test about forward declaration and specifying the underlying type:

  1. The following code is compiled OK.
    typedef int myint;
    enum T ;
    void foo(T * tp )
    {
        * tp = (T)0x12345678;
    }
    enum T : char
    {
        A
    };

But I got the warning for /W4 (/W3 does not incur this warning)

warning C4480: nonstandard extension used: specifying underlying type for enum 'T'

  1. VC++ (Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86) looks buggy in the above case:
  • when seeing enum T; VC assumes the enum type T uses default 4 bytes int as underlying type, so the generated assembly code is:
    ?foo@@YAXPAW4T@@@Z PROC                    ; foo
    ; File e:\work\c_cpp\cpp_snippet.cpp
    ; Line 13
        push    ebp
        mov    ebp, esp
    ; Line 14
        mov    eax, DWORD PTR _tp$[ebp]
        mov    DWORD PTR [eax], 305419896        ; 12345678H
    ; Line 15
        pop    ebp
        ret    0
    ?foo@@YAXPAW4T@@@Z ENDP                    ; foo

The above assembly code is extracted from /Fatest.asm directly, not my personal guess.

Do you see the

mov DWORD PTR[eax], 305419896        ; 12345678H

line?

the following code snippet proves it:

    int main(int argc, char *argv)
    {
        union {
            char ca[4];
            T t;
        }a;
        a.ca[0] = a.ca[1] = a.[ca[2] = a.ca[3] = 1;
        foo( &a.t) ;
        printf("%#x, %#x, %#x, %#x\n",  a.ca[0], a.ca[1], a.ca[2], a.ca[3] );
        return 0;
    }

The result is:

0x78, 0x56, 0x34, 0x12

  • After removing the forward declaration of enum T and move the definition of function foo after the enum T's definition: the result is OK:

The above key instruction becomes:

mov BYTE PTR [eax], 120 ; 00000078H

The final result is:

0x78, 0x1, 0x1, 0x1

Note the value is not being overwritten.

So using of the forward-declaration of enum in VC++ is considered harmful.

BTW, to not surprise, the syntax for declaration of the underlying type is same as its in C#. In pratice I found it's worth to save three bytes by specifying the underlying type as char when talking to the embedded system, which is memory limited.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
zhaorufei
  • 2,045
  • 19
  • 18
1

In my projects, I adopted the Namespace-Bound Enumeration technique to deal with enums from legacy and 3rd-party components. Here is an example:

forward.h:

namespace type
{
    class legacy_type;
    typedef const legacy_type& type;
}

enum.h:

// May be defined here or pulled in via #include.
namespace legacy
{
    enum evil { x , y, z };
}


namespace type
{
    using legacy::evil;

    class legacy_type
    {
    public:
        legacy_type(evil e)
            : e_(e)
        {}

        operator evil() const
        {
            return e_;
        }

    private:
        evil e_;
    };
}

foo.h:

#include "forward.h"

class foo
{
public:
    void f(type::type t);
};

foo.cc:

#include "foo.h"

#include <iostream>
#include "enum.h"

void foo::f(type::type t)
{
    switch (t)
    {
        case legacy::x:
            std::cout << "x" << std::endl;
            break;
        case legacy::y:
            std::cout << "y" << std::endl;
            break;
        case legacy::z:
            std::cout << "z" << std::endl;
            break;
        default:
            std::cout << "default" << std::endl;
    }
}

main.cc:

#include "foo.h"
#include "enum.h"

int main()
{
    foo fu;
    fu.f(legacy::x);

    return 0;
}

Note that the foo.h header does not have to know anything about legacy::evil. Only the files that use the legacy type legacy::evil (here: main.cc) need to include enum.h.

mavam
  • 12,242
  • 10
  • 53
  • 87
0

To anyone facing this for iOS/Mac/Xcode,

If you are facing this while integrating C/C++ headers in XCode with Objective-C, just change the extension of your file from .mm to .m

Pankaj Gaikar
  • 2,357
  • 1
  • 23
  • 29
0

My solution to your problem would be to either:

1 - use int instead of enums: Declare your ints in an anonymous namespace in your CPP file (not in the header):

namespace
{
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
}

As your methods are private, no one will mess with the data. You could even go further to test if someone sends you an invalid data:

namespace
{
   const int FUNCTIONALITY_begin = 0 ;
   const int FUNCTIONALITY_NORMAL = 0 ;
   const int FUNCTIONALITY_RESTRICTED = 1 ;
   const int FUNCTIONALITY_FOR_PROJECT_X = 2 ;
   const int FUNCTIONALITY_end = 3 ;

   bool isFunctionalityCorrect(int i)
   {
      return (i >= FUNCTIONALITY_begin) && (i < FUNCTIONALITY_end) ;
   }
}

2 : create a full class with limited const instantiations, like done in Java. Forward declare the class, and then define it in the CPP file, and instanciate only the enum-like values. I did something like that in C++, and the result was not as satisfying as desired, as it needed some code to simulate an enum (copy construction, operator =, etc.).

3 : As proposed before, use the privately declared enum. Despite the fact an user will see its full definition, it won't be able to use it, nor use the private methods. So you'll usually be able to modify the enum and the content of the existing methods without needing recompiling of code using your class.

My guess would be either the solution 3 or 1.

paercebal
  • 81,378
  • 38
  • 130
  • 159
-1

You define an enumeration to restrict the possible values of elements of the type to a limited set. This restriction is to be enforced at compile time.

When forward declaring the fact that you will use a 'limited set' later on doesn't add any value: subsequent code needs to know the possible values in order to benefit from it.

Although the compiler is concerned about the size of the enumerated type, the intent of the enumeration gets lost when you forward declare it.

xtofl
  • 40,723
  • 12
  • 105
  • 192
  • 2
    No, subsequent code need not know the values for this to be useful -- in particular, if the subsequent code is merely a function prototype taking or returning enum parameters, the size of the type is not important. Using forward declaration here can remove build dependencies, speeding compilation. – j_random_hacker Mar 26 '09 at 09:32
  • You're right. The intent is not to obey the values, but the type. Solved with 0x Enum types. – xtofl Mar 26 '09 at 10:24
-1

Because the enum can be an integral size of varying size (the compiler decides which size a given enum has), the pointer to the enum can also have varying size, since it's an integral type (chars have pointers of a different size on some platforms for instance).

So the compiler can't even let you forward-declare the enum and user a pointer to it, because even there, it needs the size of the enum.

Carl Seleborg
  • 13,125
  • 11
  • 58
  • 70
-2

This way we can forward declare enum

enum A : int;

please refer the link for details.

  • This additional "answer" does not provide more information than the [existing answer](https://stackoverflow.com/a/1280969/480982) from 2009. – Thomas Weller Dec 02 '22 at 11:37