Elaborated type specifier

From cppreference.com
< cpp‎ | language
 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
decltype (C++11)
auto (C++11)
alignas (C++11)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Implicit conversions - Explicit conversions
static_cast - dynamic_cast
const_cast - reinterpret_cast
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static
Special member functions
Templates
Miscellaneous
 
 

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.

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
attr - (since C++11) any number of attributes
1) Elaborated type specifier for a class type.
2) Elaborated type specifier for an enumeration type.
3) A declaration that consists solely of an elaborated type specifier always declares a class type named by identifier in the scope that contains the declaration.

Opaque enum declaration resembles form (3), but the enum type is a complete type after an opaque enum declaration.

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 identifier 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 elaborated-type-specifier is introduced by class, struct, or union (i.e. not by enum), and class-name is an unqualified identifier, then the elaborated-type-specifier is a class declaration of the class-name.

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 appear 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 or struct class-key must be used to refer to a non-union class type (the keywords class and struct 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 an elaborated type specifier
 
struct A {};
class A a; // OK

When used as a template argument, class T is a type template parameter named T, not an unnamed non-type parameter whose type T is introduced by elaborated type specifier.

References

  • C++23 standard (ISO/IEC 14882:2023):
  • 6.5.6 Elaborated type specifiers [basic.lookup.elab]
  • 9.2.9.4 Elaborated type specifiers [dcl.type.elab]
  • C++20 standard (ISO/IEC 14882:2020):
  • 6.5.4 Elaborated type specifiers [basic.lookup.elab]
  • 9.2.8.3 Elaborated type specifiers [dcl.type.elab]
  • C++17 standard (ISO/IEC 14882:2017):
  • 6.4.4 Elaborated type specifiers [basic.lookup.elab]
  • 10.1.7.3 Elaborated type specifiers [dcl.type.elab]
  • C++14 standard (ISO/IEC 14882:2014):
  • 3.4.4 Elaborated type specifiers [basic.lookup.elab]
  • 7.1.6.3 Elaborated type specifiers [dcl.type.elab]
  • 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]