Difference between revisions of "cpp/language/data members"
(→Layout: mention standard-layout) |
(Added CWG issue #80 DR item.) |
||
Line 6: | Line 6: | ||
class S | class S | ||
{ | { | ||
− | int n; | + | int n; // non-static data member |
− | int& r; | + | int& r; // non-static data member of reference type |
− | int a[2] = {1, 2}; | + | int a[2] = {1, 2}; // non-static data member with default member initializer (C++11) |
− | std::string s, *ps; | + | std::string s, *ps; // two non-static data members |
− | struct NestedS { | + | struct NestedS |
+ | { | ||
std::string s; | std::string s; | ||
− | } d5; | + | } d5; // non-static data member of nested type |
− | char bit : 2; | + | char bit : 2; // two-bit bitfield |
}; | }; | ||
}} | }} | ||
Line 79: | Line 80: | ||
int n; | int n; | ||
std::string s; | std::string s; | ||
− | S() : n(7) // direct-initializes n, default-initializes s | + | S() : n(7) {} // direct-initializes n, default-initializes s |
− | + | ||
}; | }; | ||
}} | }} | ||
Line 91: | Line 91: | ||
int n = 7; | int n = 7; | ||
std::string s{'a', 'b', 'c'}; | std::string s{'a', 'b', 'c'}; | ||
− | S() // default member initializer will copy-initialize n, list-initialize s | + | S() {} // default member initializer will copy-initialize n, list-initialize s |
− | + | ||
}; | }; | ||
}} | }} | ||
Line 104: | Line 103: | ||
{ | { | ||
int n = ++x; | int n = ++x; | ||
− | S() { } // uses default member initializer | + | S() {} // uses default member initializer |
− | S(int arg) : n(arg) { } // uses member initializer | + | S(int arg) : n(arg) {} // uses member initializer |
}; | }; | ||
Line 123: | Line 122: | ||
Members of array type cannot deduce their size from member initializers: | Members of array type cannot deduce their size from member initializers: | ||
{{source|1= | {{source|1= | ||
− | struct X { | + | struct X |
− | int a[] = {1,2,3}; // error | + | { |
− | int b[3] = {1,2,3}; // OK | + | int a[] = {1, 2, 3}; // error |
+ | int b[3] = {1, 2, 3}; // OK | ||
}; | }; | ||
}} | }} | ||
Line 131: | Line 131: | ||
Default member initializers are not allowed to cause the implicit definition of a defaulted {{rlp|default constructor}} for the enclosing class or the exception specification of that constructor{{mark unreviewed dr|CWG|1397}}: | Default member initializers are not allowed to cause the implicit definition of a defaulted {{rlp|default constructor}} for the enclosing class or the exception specification of that constructor{{mark unreviewed dr|CWG|1397}}: | ||
{{source|1= | {{source|1= | ||
− | struct node { | + | struct node |
+ | { | ||
node* p = new node; // error: use of implicit or defaulted node::node() | node* p = new node; // error: use of implicit or defaulted node::node() | ||
}; | }; | ||
Line 140: | Line 141: | ||
struct A | struct A | ||
{ | { | ||
− | A() = default; | + | A() = default; // OK |
− | A(int v) : v(v) { } | + | A(int v) : v(v) {} // OK |
− | const int& v = 42; | + | const int& v = 42; // OK |
}; | }; | ||
+ | |||
A a1; // error: ill-formed binding of temporary to reference | A a1; // error: ill-formed binding of temporary to reference | ||
A a2(1); // OK (default member initializer ignored because v appears in a constructor) | A a2(1); // OK (default member initializer ignored because v appears in a constructor) | ||
Line 155: | Line 157: | ||
struct A; | struct A; | ||
extern A a; | extern A a; | ||
+ | |||
struct A | struct A | ||
{ | { | ||
− | const A& a1{ A{a, a} }; // OK | + | const A& a1{A{a, a}<!-- -->}; // OK |
− | const A& a2{ A{} }; // error | + | const A& a2{A{}<!-- -->}; // error |
}; | }; | ||
− | A a{a, a}; | + | |
+ | A a{a, a}; // OK | ||
}} | }} | ||
}} | }} | ||
Line 188: | Line 192: | ||
void f(); | void f(); | ||
}; | }; | ||
+ | |||
int S::*p = &S::m; // OK: use of m to make a pointer to member | int S::*p = &S::m; // OK: use of m to make a pointer to member | ||
void (S::*fp)() = &S::f; // OK: use of f to make a pointer to member | void (S::*fp)() = &S::f; // OK: use of f to make a pointer to member | ||
Line 199: | Line 204: | ||
static const std::size_t sz = sizeof m; // OK: m in unevaluated operand | static const std::size_t sz = sizeof m; // OK: m in unevaluated operand | ||
}; | }; | ||
+ | |||
std::size_t j = sizeof(S::m + 42); // OK: even though there is no "this" object for m | std::size_t j = sizeof(S::m + 42); // OK: even though there is no "this" object for m | ||
}} | }} | ||
Line 205: | Line 211: | ||
===Defect reports=== | ===Defect reports=== | ||
{{dr list begin}} | {{dr list begin}} | ||
+ | {{dr list item|wg=cwg|dr=80|std=C++98|before=all data members cannot have the same name<br>as the name of the class (breaks C compatibility)|after=allow non-static data members share the<br>class name if there is no user-declared ctor}} | ||
{{dr list item|wg=cwg|dr=613|std=C++98|before=unevaluated uses of non-static data members not allowed|after=such uses are allowed}} <!-- gcc in particular supports it in C++03 more and defended this support as intentional in bug report 65890 --> | {{dr list item|wg=cwg|dr=613|std=C++98|before=unevaluated uses of non-static data members not allowed|after=such uses are allowed}} <!-- gcc in particular supports it in C++03 more and defended this support as intentional in bug report 65890 --> | ||
{{dr list item|wg=cwg|dr=1397|std=C++11|before=class was regarded as complete<br>in the default member initializers|after=default member init cannot trigger<br>definition of default ctor}} | {{dr list item|wg=cwg|dr=1397|std=C++11|before=class was regarded as complete<br>in the default member initializers|after=default member init cannot trigger<br>definition of default ctor}} | ||
− | {{dr list item|wg=cwg|dr=1696|std=C++98|before=reference members could be initialized to<br> | + | {{dr list item|wg=cwg|dr=1696|std=C++98|before=reference members could be initialized to temporaries<br>(whose lifetime would end at the end of ctor)|after=such init is ill-formed}} |
{{dr list item|wg=cwg|dr=1719|std=C++98|before=differently cv-qualified types weren't layout-compatible|after=cv-quals ignored, spec improved}} | {{dr list item|wg=cwg|dr=1719|std=C++98|before=differently cv-qualified types weren't layout-compatible|after=cv-quals ignored, spec improved}} | ||
{{dr list item|wg=cwg|dr=2254|std=C++11|before=pointer to standard-layout class with no data members<br>can be reinterpret_cast to its first base class|after=can be reinterpret_cast to<br>any of its base classes}} | {{dr list item|wg=cwg|dr=2254|std=C++11|before=pointer to standard-layout class with no data members<br>can be reinterpret_cast to its first base class|after=can be reinterpret_cast to<br>any of its base classes}} |
Revision as of 21:44, 8 December 2021
Non-static data members are declared in a member specification of a class.
class S { int n; // non-static data member int& r; // non-static data member of reference type int a[2] = {1, 2}; // non-static data member with default member initializer (C++11) std::string s, *ps; // two non-static data members struct NestedS { std::string s; } d5; // non-static data member of nested type char bit : 2; // two-bit bitfield };
Any simple declarations are allowed, except
|
(since C++11) |
- incomplete types, abstract class types, and arrays thereof are not allowed: in particular, a class
C
cannot have a non-static data member of typeC
, although it can have a non-static data member of typeC&
(reference to C) orC*
(pointer to C); - a non-static data member cannot have the same name as the name of the class if at least one user-declared constructor is present;
|
(since C++11) |
In addition, bit field declarations are allowed.
Contents |
Layout
When an object of some class C
is created, each non-static data member of non-reference type is allocated in some part of the object representation of C
. Whether reference members occupy any storage is implementation-defined, but their storage duration is the same as that of the object in which they are members.
For non-union class types, non-zero-sized(since C++20) members not separated by an access specifier(until C++11)with the same member access(since C++11) are always allocated so that the members declared later have higher addresses within a class object. Members separated by an access specifier(until C++11)with different access control(since C++11) are allocated in unspecified order (the compiler may group them together). |
(until C++23) |
For non-union class types, non-zero-sized members are always allocated so that the members declared later have higher addresses within a class object. Note that access control of member still affects the standard-layout property (see below). |
(since C++23) |
Alignment requirements may necessitate padding between members, or after the last member of a class.
Standard layout
A class is considered to be standard-layout and to have properties described below if and only if it is a PODType. |
(until C++11) |
A class where all non-static data members have the same access control and certain other conditions are satisfied is known as standard layout type (see StandardLayoutType for the list of requirements). |
(since C++11) |
Two standard-layout non-union class types may have a common initial sequence of non-static data members and bit-fields, for a sequence of one or more initial members (in order of declaration), if the members have layout-compatible types either both declared with [[no_unique_address]]
attribute or declared without the attribute(since C++20), and if they are bit-fields, they have the same width.
struct A { int a; char b; }; struct B { const int b1; volatile char b2; }; // A and B's common initial sequence is A.a, A.b and B.b1, B.b2 struct C { int c; unsigned : 0; char b; }; // A and C's common initial sequence is A.a and C.c struct D { int d; char b : 4; }; // A and D's common initial sequence is A.a and D.d struct E { unsigned int e; char b; }; // A and E's common initial sequence is empty
Two standard-layout non-union class types are called layout-compatible if they are the same type ignoring cv-qualifiers, if any, are layout-compatible enumerations (i.e. enumerations with the same underlying type), or if their common initial sequence consists of every non-static data member and bit field (in the example above, A
and B
are layout-compatible)
Two standard-layout unions are called layout-compatible if they have the same number of non-static data members and corresponding non-static data members (in any order) have layout-compatible types.
Standard layout types have the following special properties:
- In a standard-layout union with an active member of non-union class type
T1
, it is permitted to read a non-static data memberm
of another union member of non-union class typeT2
providedm
is part of the common initial sequence ofT1
andT2
(except that reading a volatile member through non-volatile glvalue is undefined). - A pointer to an object of standard-layout class type can be reinterpret_cast to pointer to its first non-static non-bitfield data member (if it has non-static data members) or otherwise any of its base class subobjects (if it has any)(since C++11), and vice versa. In other words, padding is not allowed before the first data member of a standard-layout type. Note that strict aliasing rules still apply to the result of such cast.
- The macro offsetof may be used to determine the offset of any member from the beginning of a standard-layout class.
- In a standard-layout union with an active member of non-union class type
Member initialization
Non-static data members may be initialized in one of two ways:
struct S { int n; std::string s; S() : n(7) {} // direct-initializes n, default-initializes s };
2) Through a default member initializer, which is a brace or equals initializer included in the member declaration and is used if the member is omitted from the member initializer list of a constructor.
struct S { int n = 7; std::string s{'a', 'b', 'c'}; S() {} // default member initializer will copy-initialize n, list-initialize s }; If a member has a default member initializer and also appears in the member initialization list in a constructor, the default member initializer is ignored for that constructor. Run this code #include <iostream> int x = 0; struct S { int n = ++x; S() {} // uses default member initializer S(int arg) : n(arg) {} // uses member initializer }; int main() { std::cout << x << '\n'; // prints 0 S s1; // default initializer ran std::cout << x << '\n'; // prints 1 S s2(7); // default initializer did not run std::cout << x << '\n'; // prints 1 }
Members of array type cannot deduce their size from member initializers: struct X { int a[] = {1, 2, 3}; // error int b[3] = {1, 2, 3}; // OK }; Default member initializers are not allowed to cause the implicit definition of a defaulted default constructor for the enclosing class or the exception specification of that constructor: struct node { node* p = new node; // error: use of implicit or defaulted node::node() }; Reference members cannot be bound to temporaries in a default member initializer (note; same rule exists for member initializer lists) struct A { A() = default; // OK A(int v) : v(v) {} // OK const int& v = 42; // OK }; A a1; // error: ill-formed binding of temporary to reference A a2(1); // OK (default member initializer ignored because v appears in a constructor) // however a2.v is a dangling reference |
(since C++11) |
It is an error if a default member initializer has a subexpression that would execute aggregate initialization which would use the same initializer: struct A; extern A a; struct A { const A& a1{A{a, a}}; // OK const A& a2{A{}}; // error }; A a{a, a}; // OK |
(since C++14) |
Usage
The name of a non-static data member or a non-static member function can only appear in the following three situations:
this->
member access expressions that appear when a non-static member name is used in any of the contexts where this is allowed (inside member function bodies, in member initializer lists, in the in-class default member initializers).
struct S { int m; int n; int x = m; // OK: implicit this-> allowed in default initializers (C++11) S(int i) : m(i), n(m) // OK: implicit this-> allowed in member initializer lists { this->f(); // explicit member access expression f(); // implicit this-> allowed in member function bodies } void f(); };
struct S { int m; void f(); }; int S::*p = &S::m; // OK: use of m to make a pointer to member void (S::*fp)() = &S::f; // OK: use of f to make a pointer to member
struct S { int m; static const std::size_t sz = sizeof m; // OK: m in unevaluated operand }; std::size_t j = sizeof(S::m + 42); // OK: even though there is no "this" object for m
Defect reports
The following behavior-changing defect reports were applied retroactively to previously published C++ standards.
DR | Applied to | Behavior as published | Correct behavior |
---|---|---|---|
CWG 80 | C++98 | all data members cannot have the same name as the name of the class (breaks C compatibility) |
allow non-static data members share the class name if there is no user-declared ctor |
CWG 613 | C++98 | unevaluated uses of non-static data members not allowed | such uses are allowed |
CWG 1397 | C++11 | class was regarded as complete in the default member initializers |
default member init cannot trigger definition of default ctor |
CWG 1696 | C++98 | reference members could be initialized to temporaries (whose lifetime would end at the end of ctor) |
such init is ill-formed |
CWG 1719 | C++98 | differently cv-qualified types weren't layout-compatible | cv-quals ignored, spec improved |
CWG 2254 | C++11 | pointer to standard-layout class with no data members can be reinterpret_cast to its first base class |
can be reinterpret_cast to any of its base classes |
See also
classes | |
static members | |
non-static member functions | |
(C++11) |
checks if a type is a standard-layout type (class template) |
byte offset from the beginning of a standard-layout type to specified member (function macro) |