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