Elaborated type specifier
Elaborated type specifiers may be used to refer to a previously-declared class name (class, struct, or union) or to a previously-declared enum name even if the name was hidden by a non-type declaration. They may also be used to declare new class names.
[edit] Syntax
class-key class-name | (1) | ||||||||
enum enum-name
|
(2) | ||||||||
class-key attr(optional) identifier ;
|
(3) | ||||||||
class-key | - | one of class, struct, union |
class-name | - | the name of a previously-declared class type, optionally qualified, or an identifier not previously declared as a type name |
enum-name | - | the name of a previously-declared enumeration type, optionally qualified |
Opaque enum declaration resembles form (3), but the enum type is a complete type after an opaque enum declaration.
[edit] Explanation
Form (3) is a special case of elaborated type specifier, usually referred to as forward declaration of classes, for the description of form (3), see Forward declaration. The following only apply to form (1) and (2).
The class-name or enum-name in the elaborated type specifier may either be a simple identifer or be a qualified-id. The name is looked up using unqualified name lookup or qualified name lookup, depending on their appearance. But in either case, non-type names are not considered.
class T { public: class U; private: int U; }; int main() { int T; T t; // error: the local variable T is found class T t; // OK: finds ::T, the local variable T is ignored T::U* u; // error: lookup of T::U finds the private data member class T::U* u; // OK: the data member is ignored }
If the name lookup does not find a previously declared type name
- The the elaborated-type-specifier is a declaration that introduces the class-name if both of the following are true:
- the elaborated-type-specifier is introduced by the class-key
- class-name is an identifier
- Otherwise the program is ill-formed (a compile error is produced).
template <typename T> struct Node { struct Node* Next; // OK: lookup of Node finds the injected-class-name struct Data* Data; // OK: declares type Data at global scope // and also declares the data member Data friend class ::List; // error: cannot introduce a qualified name enum Kind* kind; // error: cannot introduce an enum }; Data* p; // OK: struct Data has been declared
If the name refers to a typedef name, a type alias, a template type parameter, or an alias template specialization, the program is ill-formed, otherwise the elaborated type specifier introduces the name into the declaration the same way a simple type specifier introduces its type-name.
template <typename T> class Node { friend class T; // error: type parameter cannot be used in an elaborated type specifier }; class A {}; enum b { f, t }; int main() { class A a; // OK: equivalent to 'A a;' enum b flag; // OK: equivalent to 'b flag;' }
The class-key or enum
keyword present in the elaborated-type-specifier must agree in kind with the declaration to which the name in the elaborated-type-specifier refers.
- the
enum
keyword must be used to refer to an enumeration type (whether scoped or unscoped) - the
union
class-key must be used to refer to a union - either the
class
orstruct
class-key must be used to refer to a non-union class type (the keywordsclass
andstruct
are interchangeable here).
enum class E { a, b }; enum E x = E::a; // OK enum class E y = E::b; // error: 'enum class' cannot introduce a elaborated type specifier struct A {}; class A a; // OK
[edit] References
- C++11 standard (ISO/IEC 14882:2011):
-
- 3.4.4 Elaborated type specifiers [basic.lookup.elab]
-
- 7.1.6.3 Elaborated type specifiers [dcl.type.elab]
- C++98 standard (ISO/IEC 14882:1998):
-
- 3.4.4 Elaborated type specifiers [basic.lookup.elab]
-
- 7.1.5.3 Elaborated type specifiers [dcl.type.elab]
This section is incomplete Reason: probably pull most of 9.1[class.name]/2-3 out of cpp/language/class |