class-name: identifier simple-template-idClass-specifiers and elaborated-type-specifiers are used to make class-names.
class-specifier: class-head { member-specification }
class-head: class-key attribute-specifier-seq class-head-name class-virt-specifier base-clause class-key attribute-specifier-seq base-clause
class-head-name: nested-name-specifier class-name
class-virt-specifier: final
class-key: class struct unionA class-specifier whose class-head omits the class-head-name defines an unnamed class.
struct A; struct A final {}; // OK: definition of struct A, // not value-initialization of variable final struct X { struct C { constexpr operator int() { return 5; } }; struct B final : C{}; // OK: definition of nested class B, // not declaration of a bit-field member final };
struct B { int i; }; // standard-layout class struct C : B { }; // standard-layout class struct D : C { }; // standard-layout class struct E : D { char : 4; }; // not a standard-layout class struct Q {}; struct S : Q { }; struct T : Q { }; struct U : S, T { }; // not a standard-layout class
struct N { // neither trivial nor standard-layout int i; int j; virtual ~N(); }; struct T { // trivial but not standard-layout int i; private: int j; }; struct SL { // standard-layout but not trivial int i; int j; ~SL(); }; struct POD { // both trivial and standard-layout int i; int j; };
struct X { int a; }; struct Y { int a; }; X a1; Y a2; int a3;
a1 = a2; // error: Y assigned to X a1 = a3; // error: int assigned to Xare type mismatches, and that
int f(X); int f(Y);declare an overloaded (Clause [over]) function f() and not simply a single function f() twice.
struct S { int a; };
struct S { int a; }; // error, double definition
struct stat { // ... }; stat gstat; // use plain stat to define variable int stat(struct stat*); // redeclare stat as function void f() { struct stat* ps; // struct prefix needed to name struct stat stat(ps); // call stat() }
struct s { int a; }; void g() { struct s; // hide global struct s with a block-scope declaration s* p; // refer to local struct s struct s { char* p; }; // define local struct s struct s; // redeclaration, has no effect }
struct s { int a; }; void g(int s) { struct s* p = new struct s; // global s p->a = s; // parameter s }
class A * A;
member-specification: member-declaration member-specification access-specifier : member-specification
member-declaration: attribute-specifier-seq decl-specifier-seq member-declarator-list ; function-definition using-declaration static_assert-declaration template-declaration deduction-guide alias-declaration empty-declaration
member-declarator-list: member-declarator member-declarator-list , member-declarator
member-declarator: declarator virt-specifier-seq pure-specifier declarator brace-or-equal-initializer identifier attribute-specifier-seq : constant-expression
virt-specifier-seq: virt-specifier virt-specifier-seq virt-specifier
virt-specifier: override final
pure-specifier: = 0
struct S { using T = void(); T * p = 0; // OK: brace-or-equal-initializer virtual T f = 0; // OK: pure-specifier };
struct tnode { char tword[20]; int count; tnode* left; tnode* right; };
tnode s, *sp;declares s to be a tnode and sp to be a pointer to a tnode.
struct A { int a; char b; }; struct B { const int b1; volatile char b2; }; struct C { int c; unsigned : 0; char b; }; struct D { int d; char b : 4; }; struct E { unsigned int e; char b; };
struct T1 { int a, b; }; struct T2 { int c; double d; }; union U { T1 t1; T2 t2; }; int f() { U u = { { 1, 2 } }; // active member is t1 return u.t2.c; // OK, as if u.t1.a were nominated }
struct X { typedef int T; static T count; void f(T); }; void X::f(T t = count) { }
typedef void fv(); typedef void fvc() const; struct S { fv memfunc1; // equivalent to: void memfunc1(); void memfunc2(); fvc memfunc3; // equivalent to: void memfunc3() const; }; fv S::* pmfv1 = &S::memfunc1; fv S::* pmfv2 = &S::memfunc2; fvc S::* pmfv3 = &S::memfunc3;
struct tnode { char tword[20]; int count; tnode* left; tnode* right; void set(const char*, tnode* l, tnode* r); }; void tnode::set(const char* w, tnode* l, tnode* r) { count = strlen(w)+1; if (sizeof(tword)<=count) perror("tnode string too long"); strcpy(tword,w); left = l; right = r; } void f(tnode n1, tnode n2) { n1.set("abc",&n2,0); n2.set("def",0,0); }
struct s {
int a;
int f() const;
int g() { return a++; }
int h() const { return a++; } // error
};
int s::f() const { return a; }
struct process { static void reschedule(); }; process& g(); void f() { process::reschedule(); // OK: no object necessary g().reschedule(); // g() is called }
int g();
struct X {
static int g();
};
struct Y : X {
static int i;
};
int Y::i = g(); // equivalent to Y::g();
class process { static process* run_chain; static process* running; }; process* process::running = get_main(); process* process::run_chain = running;
identifier attribute-specifier-seq : constant-expressionspecifies a bit-field; its length is set off from the bit-field name by a colon.
enum BOOL { FALSE=0, TRUE=1 }; struct A { BOOL b:1; }; A a; void f() { a.b = TRUE; if (a.b == TRUE) // yields true { /* ... */ } }
int x; int y; struct enclose { int x; static int s; struct inner { void f(int i) { int a = sizeof(x); // OK: operand of sizeof is an unevaluated operand x = i; // error: assign to enclose::x s = i; // OK: assign to enclose::s ::x = i; // OK: assign to global x y = i; // OK: assign to global y } void g(enclose* p, int i) { p->x = i; // OK: assign to enclose::x } }; }; inner* p = 0; // error: inner not in scope
struct enclose {
struct inner {
static int x;
void f(int i);
};
};
int enclose::inner::x = 1;
void enclose::inner::f(int i) { /* ... */ }
class E { class I1; // forward declaration of nested class class I2; class I1 { }; // definition of nested class }; class E::I2 { }; // definition of nested class
struct X { typedef int I; class Y { /* ... */ }; I a; }; I b; // error Y c; // error X::Y d; // OK X::I e; // OK
union U { int i; float f; std::string s; };
union A { int x; int y[4]; }; struct B { A a; }; union C { B b; int k; }; int f() { C c; // does not start lifetime of any union member c.b.a.y[3] = 4; // OK: S(c.b.a.y[3]) contains c.b and c.b.a.y; // creates objects to hold union members c.b and c.b.a.y return c.b.a.y[3]; // OK: c.b.a.y refers to newly created object (see [basic.life]) } struct X { const int a; int b; }; union Y { X x; int k; }; void g() { Y y = { { 1, 2 } }; // OK, y.x is active union member ([class.mem]) int n = y.x.a; y.k = 4; // OK: ends lifetime of y.x, y.k is active member of union y.x.b = n; // undefined behavior: y.x.b modified outside its lifetime, // S(y.x.b) is empty because X's default constructor is deleted, // so union member y.x's lifetime does not implicitly start }
u.m.~M(); new (&u.n) N;
union { member-specification } ;is called an anonymous union; it defines an unnamed type and an unnamed object of that type called an anonymous union object.
union U {
int x = 0;
union {
int k;
};
union {
int z;
int y = 1; // error: initialization for second variant member of U
};
};
int x; void f() { static int s; int x; const int N = 5; extern int q(); struct local { int g() { return x; } // error: odr-use of automatic variable x int h() { return s; } // OK int k() { return ::x; } // OK int l() { return q(); } // OK int m() { return N; } // OK: not an odr-use int* n() { return &N; } // error: odr-use of automatic variable N }; } local* p = 0; // error: local not in scope