This question already has an answer here:
C++ Forward declaring class scoped enumeration
(1 answer)
Closed 6 years ago.
I know that in C++11 it's possible to forward declare an enum type (if storage type is provided) e.g.
enum E : short;
void foo(E e);
....
enum E : short
{
VALUE_1,
VALUE_2,
....
}
But I would like to forward declare an enum defined within a class e.g.
enum Foo::E : short;
void foo(E e);
....
class Foo
{
enum E : short
{
VALUE_1,
VALUE_2,
....
}
}
Is something like this possible in C++11 ?
No, such a forward declaration isn't possible. [decl.enum]/5 (bold emphasis mine):
If the enum-key is followed by a nested-name-specifier, the
enum-specifier shall refer to an enumeration that was previously
declared directly in the class or namespace to which the
nested-name-specifier refers (i.e., neither inherited nor introduced
by a using-declaration), and the enum-specifier shall appear in a
namespace enclosing the previous declaration.
(In this case the nested-name-specifier would be the name of your class followed by a ::.)
You could, though, put the enumeration outside and use an opaque-enum-declaration.
As #Columbo says, you can't declare it in the form you specify.
You can, however, forward declare the nested enum inside the class declaration:
class Foo
{
enum E : short;
};
void foo(Foo::E e);
enum Foo::E : short
{
VALUE_1,
VALUE_2,
....
};
Whether you gain any benefit by doing so depends, of course, on the circumstances.
Related
I just found this weird piece of code in the align_val_t definition of Visual Studio 2019's standard library:
namespace std
{
enum class align_val_t : size_t {};
}
What does the colon mean?
enum class align_val_t : size_t {};
// ^
// this thing
All standard references below refers to N4659: March 2017 post-Kona working draft/C++17 DIS.
The enum Name : UnderlyingType {} syntax defines an enum that is said to be fixed, with an explicitly specified underlying type. From [dcl.enum]/5:
Each enumeration defines a type that is different from all other types. Each enumeration also has an underlying type. The underlying type can be explicitly specified using an enum-base. For a scoped enumeration type, the underlying type is int if it is not explicitly specified. In both of these cases, the underlying type is said to be fixed. [...]
enum class align_val_t : size_t {};
// ^
// this thing
In this particular example, an enum named align_val_t is defined (in the std namespace) as fixed with explicitly specified underlying type size_t (std::size_t, to be precise).
Scoped and unscoped enumerations & underlying types
// Unscoped enumeration; [enum Name {}]
// - underlying type not fixed.
enum UnscopedUnfixed { a, b };
auto uu_a = a;
// Scoped enumeration; [enum class Name {} / enum struct Name {}]
// - underlying type implicitly fixed to int.
enum class ScopedImplicitlyFixed { c, d };
auto sif_c = ScopedImplicitlyFixed::c;
// Scoped enumeration; [enum class Name : TYPE {} / enum struct Name : TYPE {}]
// - underlying type explicitly fixed.
enum class ScopedExplicitlyFixed : unsigned int { e, f };
auto sef_e = ScopedExplicitlyFixed::e;
enum class align_val_t /*HERE----->*/:/*<------HERE*/ size_t {};
That colon is (an optional) part of the syntax of an enum (class) definition. It separates the name of the enum (class) and the underlying type.
Since C++11, this is part of the syntax of enums, for which you can (but don't have to) specify the underlying type.
It's saying that every member of the enum has the type std::size_t; exactly that type, nothing else. In the olden days, it wasn't always quite so clear what the type would be, at least not by simply looking at the code.
This question already has answers here:
Mixing class and struct
(6 answers)
Closed 9 years ago.
Suppose there is a code like this:
template <typename T>
CLASS_KEY1 X{};
PREFIX template CLASS_KEY2 X<int>;
where CLASS_KEY1, CLASS_KEY2 and PREFIX are macros. CLASS_KEY1 and CLASS_KEY2 may be expanded to class, struct or union keywords. PREFIX may be expanded to empty set of characters or extern keyword.
Here is the table which shows when such code compiles (Yes - compiles, No - does not compile) for all combinations of macros values (compiler gcc-4.8.1, option -std=c++11):
PREFIX extern extern extern
CLASS_KEY1\CLASS_KEY2 class struct union class struct union
class Yes Yes? No Yes Yes? No
struct Yes? Yes No Yes? Yes No
union No No Yes No No Yes
Is it a bug in gcc or standard requirement (strange cases are labeled with question marks)? What about other compilers?
Section 7.1.6.3 (Elaborated type specifiers) of the C++11 standard says :
The class-key or enum keyword present in the elaborated-type-specifier shall agree in kind with the declaration to which the name in the elaborated-type-specifier refers. This rule also applies to the form of elaborated-type-specifier that declares a class-name or friend class since it can be construed as referring to the definition of the class. Thus, in any elaborated-type-specifier, the enum keyword shall be used to refer to an enumeration (7.2), the union class-key shall be used to refer to a union (Clause 9), and either the class or struct class-key shall be used to refer to a class (Clause 9) declared using the class or struct class-key.
So, the behavior you're seeing is allowed.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Difference between ‘struct’ and ‘typedef struct’ in C++?
An answer to this question led me to wonder about the following:
I presume that defining a class as follows:
typedef class {int i;} C;
would be completely equivalent to defining it in the conventional manner:
class C
{
int i;
};
Is this presumption correct?
In this isolated example they are functionally the same, at least from the outside.
However there are differences. One instance in particular, you cannot declare a constructor for a struct or a class declared in this way, simply because the class is unnamed. Similarly you cannot declare any function that involves the class' name. Here are some examples:
typedef class
{
public:
Gizmo() : n_(42) {}; // NOT OK
~Gizmo();
Gizmo& operator<<(int n);
private:
int n_;
} Gizmo;
You also cannot forward declare an anonymous class:
class Gizmo;
In C++ I have never seen a case where typedefing an anonymous struct or a class is preferable to simply declaring a class or a struct that is named. In some cases the traditional method is definitely preferred. The moral of the story is: don't use typedef class {} Name; in C++. It buys you nothing, and costs you something.
I believe this is a duplicate question (can't find), but if not, observe that this compiles:
class C
{
int i;
};
void C() {}
class C x;
while this won't:
typedef class
{
int i;
} C;
void C() {}
C x;
The name spaces are different.
From a practical standpoint yes, because the standard says (9.1/5) that
A typedef-name (7.1.3) that names a class type, or a cv-qualified version thereof, is also > a class-name. If a typedef-name that names a cv-qualified class type is used where a
class-name is required, the cv-qualifiers are ignored.
7.1/3 says:
A name declared with the typedef specifier becomes a typedef-name.
Within the scope of its declaration, a typedef-name is syntactically
equivalent to a keyword and names the type associated with the
identifier in the way described in Clause 8. A typedef-name is thus a
synonym for another type.
From a theoretical standpoint no, because you could (and indeed I see people already have) draft programs that are valid or invalid depending on which version was used, since 7.1/3 continues from where I cut it off to say:
A typedef-name does
not introduce a new type the way a class declaration (9.1) or enum declaration does.
They're not equivalent. In particular,
int main()
{
class C c;
}
will only compile for one of the two definitions.
Here is yet another difference: the latter can be forward-declared, the former can't.
This is a follow-up question.
In the previous question, #JohannesSchaub-litb said that the following code is not fully standard-conformant:
class { int i; }; //unnamed-class definition. § 9/1 allows this!
and then he added,
while it is grammatically valid, it breaks the rule that such a class must declare at least one name into its enclosing scope.
I couldn't really understand this. What name is he talking about?
Could anyone elaborate on this further (preferably quoting the Standard)?
Clause 9 of the standard allows class {public: int i;} (note the lack of a final semicolon) because this decl-specifier-seq for an unnamed class might be used in some other construct such as a typedef or a variable declaration. The problem with class {public: int i;}; (note that the final semicolon is now present) is that this class specification now becomes a declaration. This is an illegal declaration per clause 7, paragraph 3 of the standard:
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.
The point is that by declaring class{ int i; }; you are assembling a bunch of symbol (int i, in this case) you will not be able to use anywhere else in whatever code.
For this code to make sense you should at least do one of the following:
class Myclass { int i; }; //I can furthermore instantiate variables of Myclass
class { int i; } myvar; //This in fact creates a myvar object
typedef class { int i; } MyType; //I can funthermore instantiate variables of MyType
By saying just class{ int i; }; you are saying to the compiler:
keep an int and name it i,
wrap it into a class I'll never call and...
forget it! (};)
If you remove that declaration from your program, nothing will change.
class { int i; }; is not a valid declaration because it is a simple-declaration without an init-declarator-list but it doesn't introduce (or re-declare) a class name.
ISO/IEC 14882:2011 7 [dcl.dcl] / 3:
In a simple-declaration, the optional init-declarator-list can be omitted only when declaring a class (Clause 9) or enumeration (7.2), that is, when the decl-specifier-seq contains either a class-specifier, an elaboratedtype-specifier with a class-key (9.1), or an enum-specifier. 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.
The error message from GCC explains it quite succinctly:
$ cat > a.cc
class { int i; };
$ g++ -Wall -std=c++98 a.cc
a.cc:1: error: abstract declarator ‘<anonymous class>’ used as declaration
class { int i; } is an abstract-declarator (Standard, §8) but not a valid declaration (§7). That's the rule that #JohannesSchaub-litb referenced: for a valid declaration, you need something to be declared, e.g. a class name or variable name.
You are breaking the the [basic.scope.pdecl]/6, which says :
The point of declaration of a class first declared in an elaborated-type-specifier is as follows:
— for a declaration of the form
class-key attribute-specifier-seqopt identifier ;
the identifier is declared to be a class-name in the scope that contains the declaration, otherwise
— for an elaborated-type-specifier of the form
class-key identifier
if the elaborated-type-specifier is used in the decl-specifier-seq or parameter-declaration-clause of a
function defined in namespace scope, the identifier is declared as a class-name in the namespace that
contains the declaration; otherwise, except as a friend declaration, the identifier is declared in the
smallest namespace or block scope that contains the declaration. [ Note: These rules also apply within
templates. — end note ] [ Note: Other forms of elaborated-type-specifier do not declare a new name,
and therefore must refer to an existing type-name. See 3.4.4 and 7.1.6.3. — end note ]
you are not creating a variable of an anonymous type
you are not create a type
There is another example (in [basic.def]/2) from the standard that proves your example is not standard compliant :
struct S { int a; int b; }; // defines S, S::a, and S::b
struct X { // defines X
int x; // defines non-static data member x
static int y; // declares static data member y
X(): x(0) { } // defines a constructor of X
};
int X::y = 1; // defines X::y
enum { up, down }; // defines up and down
namespace N { int d; } // defines N and N::d
namespace N1 = N; // defines N1
X anX; // defines anX
Your example doesn't define anything (except an anonymous struct, who's fields can not be accessed).
Note an exception about the enum, because this case introduces two values to use.
According to ($ 3.4.4) A typedef name followed by a class-key is invalid. But I'm not sure about which scope? For example: In the following, compiler doesn't complain if elaborated specifier was used in a block such as inside a function.
typedef class { /* ... */ } S;
// invalid
class S;
// ok
void foo() {
class S;
}
Is it valid to declare a class inside a local scope with typedef-name, Why?
7.1.3 paragraph 3 tells :
In a given scope, a typedef specifier shall not be used to redefine
the name of any type declared in thascope to refer to a different
type.
[Example:
class complex { /* ... */ };
typedef int complex; //
error: redefinition
Then it goes :
—end example] Similarly, in a given scope, a class or enumeration
shall not be declared with the same name as a typedef-name that is
declared in that scope and refers to a type other than the class or
enumera- tion itself. [Example:
typedef int complex;
class complex { /* ... */ };
// error: redefinition
This is your example.
The problem is that you declared the class with no name, but with an alias (the typedef). Later, you used the same name to declare without defining another class (I know that was not the intention, but that's what the compiler understood) and its name clashed with the typedef. When you did the same thing inside foo(), that was a separated scope and so was acceptable. But notice that the 'class S' inside foo() is NOT the same type as that declared in the first line.
Outside the function, you cannot declare a class with the same name as a typedef in the same namespace.
Inside the function, you are declaring a new class, scoped inside the function. It is not the same as the anonymous class declared in the surrounding namespace. Within the function, this hides the declaration of the typedef.