13

I'm working in C++11 and including an h file implemented in C++03. In the h file I'm including there's an enum Foo defined. I want to declare a forward to it in code.h and use it in code.cpp:

header.h:

enum Foo {A=1};

code.h:

enum Foo : int; // also tried : unsigned int, long, short, unsigned short, char, unsigned char
void bar(Foo foo);

code.cpp:

#include header.h
void bar(Foo foo) { }

This is the error I get when I compile (tested g++ 4.8.5 and g++ 5.3.1):

In file included from code.cpp:2:0:
header.h:1:6: error: underlying type mismatch in enum ‘enum Foo’
 enum Foo {A=1};
      ^
In file included from code.cpp:1:0:
code.h:3:12: error: previous definition here
 enum Foo : int;

I can fix this error if I change header.h to:

enum Foo : int {A=1};

But I don't own that header and can't change it. Taking the error at face value, it sounds like all I need to know is what type g++ uses for enums which don't specify underlying type, then use that type in my forward.

Even this simple example doesn't work :

#include <iostream>
#include <string>
#include <type_traits>

enum Foo {A=1};
enum Foo : unsigned; // : std::underlying_type<Foo>::type also doesn't work

int main()
{

  std::cout << "Hello, world\n";
}
ytoledano
  • 3,003
  • 2
  • 24
  • 39
  • 6
    Well, the C++ Standard explicitly forbids this (forward-declaring an enumeration requires a fixed underlying type). Maybe another solution is possible, like wrapping the enum in a struct? – dyp Mar 13 '17 at 15:04
  • Reading https://stackoverflow.com/questions/71416/forward-declaring-an-enum-in-c, I thought it was possible in C++11. – ytoledano Mar 13 '17 at 15:07
  • 2
    Since C++11, you can forward-declare an enumeration, but this requires fixing the underlying type ("specifying an enum-base"), and repeating the same underlying type for each declaration (forward-declaration or actual declaration). So if you can't add an underlying type to the external header, then you cannot (are not allowed to) forward-declare this enumeration. – dyp Mar 13 '17 at 15:10
  • `std::underlying_type` tells you the underlying type of an `enum`. What does it say for your enum? – Yakk - Adam Nevraumont Mar 13 '17 at 15:39
  • @Yakk, `unsigned`. Yet if I do `enum Foo : unsigned` - I get the error above. – ytoledano Mar 13 '17 at 15:51
  • Also this doesn't work: `enum Foo : std::underlying_type::type;` nor this: `enum Foo : std::underlying_type::type;` (where `Foo1` is just some enum) – ytoledano Mar 13 '17 at 15:54
  • Interestingly, using a scoped enum, `enum struct Foo : int` seems to work, but anything other than the type `int` throws the same error. – Nowhere Man Mar 13 '17 at 16:36
  • Related [Why must an enumeration's size be provided when it is forward declared?](http://stackoverflow.com/q/29035225/1708801) – Shafik Yaghmour Mar 13 '17 at 17:29

3 Answers3

10

There doesn't seem to be any way of doing this, even if you specify the exact same underlying type that the compiler would have chosen for your C++03-style enum.

Example: compiling the following code...

enum Foo { A=1 };
cout << typeid(typename std::underlying_type<Foo>::type).name();

...on Coliru and demangling via c++filt will print "unsigned int" both with g++ and clang++.

Even if you specify unsigned int as the explicit underlying type of your Foo forward declaration, both compilers will complain.

enum Foo : unsigned int;
void bar(Foo);

enum Foo {A=1};

main.cpp:8:6: error: enumeration previously declared with fixed underlying type
enum Foo {A=1};
     ^
main.cpp:5:6: note: previous declaration is here
enum Foo : unsigned int;
     ^

This is because both the forward declaration and the "real" enum declaration need to have the same explicit underlying type, even if you manage to "guess" what the compiler would have chosen for you.


tl;dr: you can only forward-declare an enum if both the forward-declaration and the real declaration have the same explicitly specified underlying type.

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • By the way, VisualStudio (can't remember which version) accepted mismatches between specifications of the underlying type (as an extension). This leads to dangerous mismatches where the size of the enumeration is different within the same translation unit, and even incorrect code. I think it's a good idea to enforce consistency between enumeration declarations regarding the specification of an underlying type. – dyp Mar 15 '17 at 14:07
  • on g++ (7.4) the `cout` line in the example code above doesn't compile. It should be `std::underlying_type::type` not `std::underlying_type_t::type` – virgesmith Aug 09 '19 at 12:04
6

You can only forward declare an enum if you give it a fixed underlying type in the forward declaration. Also, the definition of the enum must use the same fixed underlying type.

Your problem is that your enum definition in header.h doesn't have an underlying type, but the later forward declaration has one. They both must have one.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • I'm starting to understand that this won't work, but I don't see any reason for it not to. At least this should be possible: `enum Foo : std::underlying_type::type`. What I'm saying is that there's always an underlying type, just in C++03 style it's implicit. – ytoledano Mar 13 '17 at 16:30
  • 4
    @ytoledano: You're asking the compiler to determine the implicit underlying type of an enum that has *not yet been defined*. That's *impossible*. The compiler has not seen the definition of `Foo`, and therefore, `std::underlying_type::type` is not legal syntax. The implicit underlying type is based on the definition of `Foo`, which is in a header that you didn't include (and if you did, there's no point in forward declaring the enum). – Nicol Bolas Mar 13 '17 at 16:33
2

As of my todays version of gcc, you have to define the exact type twice, not only in the forward declaration but as well in the definition:

enum Foo : int;

...

enum Foo : int {A=1};

This worked for me, compiler is gcc 7.3.0.

emax
  • 307
  • 3
  • 7