In the following code there are no errors or warnings at compile/link-time
enum E;
enum E;
int _tmain(int argc, _TCHAR* argv[]){ }
Is it VS2010 bug?
Your code compiles with VC++ 2010 and later by virtue of MS language extensions
that you can disable with the compiler flag /Za.
At first sight,
enum E;
enum E;
looks just like two forward declarations of enum E. In that light, the duplication does not appear
relevant. N forward declarations of something are no more or less legal than one, so the effect of
enabling the MS extensions (/Ze) would appear to include enabling forward enum declarations,
which are illegal per C++03 or C++11.
But that appearance is misleading, as the following program shows:
#include <iostream>
enum E;
//enum E;
enum E e;
int main()
{
std::cout << (e == 0) << std::endl;
return e;
}
This also compiles clean with VC++ 2010 or later, proving that
the compiler parses enum E; not as forward declaration of E but as a definition.
The language extension here is that enum E;, on first sight, is equated with enum E {}.
If we uncomment //enum E; in the program, it still compiles clean, showing that on second sight
and subsequently, enum E; is not again parsed as a definition, just as a redeclaration of E.
VC++ 2013 supports the C++11 concepts of scoped and based enums. With extensions disabled,
it will compile and reject declarations as indicated by this piece of code:
enum UnscopedBasedEnum : int; // :)
enum UnscopedBasedEnum : int; // :)
UnscopedBasedEnum ube; // :)
enum struct ScopedBaselessEnum; // :)
enum struct ScopedBaselessEnum; // :)
ScopedBaselessEnum sbe;
//enum UnscopedBaseLessEnum; // :(
all of which is C++11 compliant.
The declarations of ube and sbe prove, however, that none of the
declarations here is a forward declaration, if foward declaration has the customary meaning
of declaration of an incomplete type. If UnscopedBasedEnum|ScopedBaselessEnum
was an incomplete type in these declarations then the declaration of ube|sbe would
would declare an object of incomplete type, and would not compile. Per C++11, the declarations
of UnscopedBasedEnum and ScopedBaselessEnum are opaque enum declarations, meaning
that no enumerator-list is present or implied. But they nevertheless declare complete
types: p 7.2.3
An opaque-enum-declaration is either a redeclaration of an enumeration in the current scope or a declaration
of a new enumeration. [ Note: An enumeration declared by an opaque-enum-declaration has fixed underlying
type and is a complete type. The list of enumerators can be provided in a later redeclaration with an enum-specifier.
—end note ]
What you are doing here is enum forward declaration. Some compilers (like VS) provide language extensions which enable this behaviour for pre-C++11 standard. You can verify this by disabling the language extensions in your project settings which will result in a compiler error. G++ does not have such extensions but it should compile your code with -std=c++11 after adding size specifiers or when using enum class instead.
Valid in Visual Studio with compiler extensions enabled
enum E;
enum E;
Valid in all compilers supporting C++11
enum class E;
enum class E;
// or
enum E : short;
enum E : short;
Related
You can't forward declare an enum in C++, but you can in C.
For a C code-base that uses some C++ code, is there a way to use a forward declared enum in C that doesn't cause errors when that header is used in C++ (within an extern "C" {..} block)?
Example:
extern "C" {
enum MyEnum;
}
int main() { return 0; }
GCC gives the error:
error: use of enum ‘MyEnum’ without previous declaration
enum MyEnum;
^~~~~~
Clang also fails with:
error: ISO C++ forbids forward references to 'enum' types
enum MyEnum;
To give some context, this is a mainly C code-base where a small C++ module happens to include a header for C code. I can do some hack to make C++ ignore the enum, but I would like to know if its possible for C++ to use C headers in this case.
Update: It's been noted the official C specification doesn't support this. However, it seems this is a de facto standard among some of the most widely used compilers: GCC, Clang, and Microsoft Visual C++.
You can forward-declare enum in C++ starting from C++11. It's called "opaque declaration" rather than "forward declaration" because technically it results in a bit different effect: the size of the enum is known after its opaque declaration, while with the forward-declared types that's not the case.
However, from the daily perspective it's the same idea: you can just declare your enum and use it further in the code. From cppreference:
enum-key attr(optional) nested-name-specifier(optional) identifier enum-base(optional) ;(2) (since C++11)
2) Opaque enum declaration: defines the enumeration type but not its enumerators: after this declaration, the type is a complete type and its size is known. Note: an explicit specialization declaration of a scoped enumeration member of a class template is the only case where nested-name-specifier appears before identifier (since C++14)
Forward declaration of enums in C just makes no sense. Enums in C only introduce integral enumeration constants. They don't introduce any types you would want to use. Each enum type is an unspecified integral type, compatible with all other integral types, so there isn't any type safety at all.
typedef enum e12 { ONE, TWO } e12;
typedef enum e34 { THREE, FOUR } e34;
int main () {
e12 one = ONE;
e34 three = THREE;
one = three;
three = ONE;
return one + three;
}
This C program compiles cleanly with GCC on -Wall -Wextra -Wpedantic (it won't work as a C++ program of course).
So a useful portable cross-language solution would be
#ifdef __cplusplus
enum MyEnum : int;
#else
typedef int MyEnum; // 'enum MyEnum' would give no improvement over this
#endif
This can be done in C++11, (see #vasiliy-galkin's answer), an example of how this can work for shared C/C++ headers.
C header forward declaring MyEnum.
#ifdef __cplusplus
extern "C" {
#endif
enum MyEnum
#ifdef __cplusplus
: int
#endif
;
#ifdef __cplusplus
}
#endif
What is the point of declaration of enumeration types? Is it immediately after the name of an enumeration? I saw Standard C++ 14 (n4296) §3.3.2/3:
The point of declaration for an enumeration is immediately after the
identifier (if any) in either its enum-specifier (7.2) or its first
opaque-enum-declaration (7.2), whichever comes first
But when I try to reproduce it;
template <class T>
struct CL
{
using UndType = int;
};
enum class E: CL<E>::UndType; //error: E is undefined
I have got an error on all the compilers, although enum-base for enumeration E is placed after the identifier and must be visible.
The following;
enum class E : CL<E>::UndType;
Is not accepted as a valid declaration in some current implementations (tested clang++, g++ and MSVC). They do not accept the, as yet incomplete type E, in the enum-base CL<E>::UndType. The error given in the tested implementations is that E is undeclared at that point. They seem to place the point of declaration at the end of the enum-base, they consider it declared once it is complete.
When reading the specifications;
§14.3.1/2 Template type arguments
[ Note: A template type argument may be an incomplete type (3.9). — end note ]
And
§7.2/6 Enumeration declarations
An enumeration whose underlying type is fixed is an incomplete type from its point of declaration (3.3.2) to immediately after its enum-base (if any), at which point it becomes a complete type.
Does hint at it being compilable; as is the case with CRTP implementations.
I'm note sure if this (i.e. the failure to compile enum class E : CL<E>::UndType;) is the intention or if it was considered as a use case. From the specification, the opaque enum declaration is given some "special" treatment w.r.t. its base type and the requirement that it must be an integral type.
Presumably, the code should be compilable given the resolution to CWG#1482.
As for current workarounds...
This;
enum class E; // default underlying type is int
Is the minimum declaration.
The opaque declaration could be either;
enum class E : int; // int base
The following would be a full definition (including enumerators);
enum class E : int {/*...*/};
Or to use the class template, another type (possibly void) could be used.
enum class E : CL<void>::UndType;
Now CWG2516 is opened for this.
I believe it's a bug in the Standard which forbids a portable implementation of is_scoped_enum.
Given a C++11 enum class, nested inside several long- and ugly-named namespaces:
namespace
long_and_ugly
{
enum class
colour
{
red,
green,
blue
};
}
Can aliases be made of the enumeration values? With clang++ 3.5, it is possible to do what follows:
using long_and_ugly::colour; // take all the values into the current namespace
using long_and_ugly::colour::red; // take only 'red' into the current namespace
function_taking_colour_argument( red ); // instead of fully referring to the value
g++ 4.9, however, complains. I can't copy its error message because I can't access the code, but it explicitly complained about the usage of the using directive or declaration. I have also tried this:
using red = long_and_ugly::colour::red;
But it also failed. I'm sorry for not pasting the errors. Nevertheless, I believe you should be able to reproduce it.
Question(s)
Is it possible to declare aliases to enumeration values in standard C++11, or was I using a clang extension?
If it is, what is the correct syntax?
Enumerators in using-declarations
The problem is that the standard says that you shall not refer to an enumerator inside an enum class when using specifying a using-declaration.
7.3.3p7 The using declaration [namespace.udecl] (n3337)
A using-declaration shall not name a scoped enumerator.
namespace N {
enum class E { A };
}
using N::E; // legal
using N::E::A; // ill-formed, violation of [namespace.udecl]p7
Note: clang does accept both lines above; here's a relevant bug report.
It's perfectly fine to refer to the actual name of the enum class itself, but trying to refer to one of its enumerators is ill-formed.
Enumerators in alias-declarations
The standard says that an alias-declaration can only be used to refer to a type-name, since an enumerator isn't a type, using one in such context is ill-formed.
namespace N {
enum class E { A };
}
using x = N::E; // legal, `N::E` is a type
using y = N::E::A; // ill-formed, `N::E::A` isn't a type
Alternatives to using- and alias-declarations
You could declare a constant having whatever-name-of-your-choice initialized with the value you'd like to "alias":
namespace N {
enum class E { A };
}
constexpr N::E x = N::E::A;
int main () {
N::E value = x; // semantically equivalent of `value = N::E::A`
}
Sort of:
namespace long_and_ugly {
enum class colour
{
red,
green,
blue
};
}
const colour red = long_and_ugly::colour::red;
class Test
{
enum{};
...
};
Is this empty enum definition portable? Compiles in gcc and and msvc.
such an enum is specifically listed in clause 7 paragraph 3 of the C++ standard as
ill-formed. gcc does not accept it. there was a bug fix for this in gcc:
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=29018
According to the following snippet from the c++ standard we can deduce that it's indeed a valid statement:
7.2/1 Enumeration declarations (C++03)...
enum-specifier:
enum identifieropt { enumerator-listopt }
Note that both the identifier and the enumerator-list are optional, and therefor a statement as enum {} is valid (if you ask the standard).
But doesn't the standard also say that empty declarations are ill-formed?
Yes, and there is even an example of enum { }; in the below snippet from the standard.
7/3 Specifiers (C++03)
In these cases and whenever a class-specifier or enum-specifier is
present in the decl-specifier-seq, the identifiers in these specifiers
are among the names being declared by the declaration (as class-
names, enum-names, or enumerators, depending on the syntax).
In such cases, and except for the declaration of an unnamed bit-field
(9.6), the decl-specifier-seq shall introduce one or more names into
the program, or shall redeclare a name introduced by a previous
declaration.
*Example [
enum { }; // ill-formed
typedef class { }; // ill-formed
*end example]
Conclusion
The statement seems to be ill-formed after a careful look at the standard, though compilers are written by humans - and humans tend to make mistakes and sometimes overlook things.
TL;DR You should not use an empty declaration such as enum { };, even though it compiles
We found something similar to the following (don't ask ...):
namespace N {
struct A { struct B; };
}
struct A { struct B; };
using namespace N;
struct ::A::B {}; // <- point of interest
Interestingly, this compiles fine with VS2005, icc 11.1 and Comeau (online), but fails with GCC:
global qualification of class name is invalid before '{' token
From C++03, Annex A, it seems to me like GCC is right:
the class-head can consist of nested-name-specifier and identifier
nested-name-specifier can't begin with a global qualification (::)
obviously, neither can identifier
... or am i overlooking something?
I think you are getting it right: GCC implements the standard to the letter in this case, while the others implement it less strict (have a look at issue #355).
You could do the following to work-around the limitation of the syntax
struct identity< ::A >::type::B {};
Or you use an explicit named typedef
typedef ::A AHidden;
struct AHidden::B { };
Or, of course, you exchange the order of using namespace and the nested class definition. Notice that Annex A is informative only. The normative text is at clauses 5.1/7 and 9.