Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/data members"

From cppreference.com
< cpp‎ | language
(Layout: mention standard-layout)
(Added CWG issue #80 DR item.)
Line 6: Line 6:
 
class S
 
class S
 
{
 
{
     int n;               // non-static data member
+
     int n;             // non-static data member
     int& r;               // non-static data member of reference type
+
     int& r;             // non-static data member of reference type
     int a[2] = {1, 2};   // non-static data member with default member initializer (C++11)
+
     int a[2] = {1, 2}; // non-static data member with default member initializer (C++11)
     std::string s, *ps;   // two non-static data members
+
     std::string s, *ps; // two non-static data members
     struct NestedS {
+
     struct NestedS
 +
    {
 
         std::string s;
 
         std::string s;
     } d5;                 // non-static data member of nested type
+
     } d5;               // non-static data member of nested type
     char bit : 2;         // two-bit bitfield
+
     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;         // OK
+
     A() = default;     // OK
     A(int v) : v(v) { }     // OK
+
     A(int v) : v(v) {} // OK
     const int& v = 42;     // OK
+
     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};                 // OK
+
 
 +
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>temporaries (whose lifetime would end at the end of ctor)|after=such init is ill-formed}}
+
{{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

 
 
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
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
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
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
 

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

  • thread_local storage class specifier is not allowed (but it is allowed for static data members);
(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 type C, although it can have a non-static data member of type C& (reference to C) or C* (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 member m of another union member of non-union class type T2 provided m is part of the common initial sequence of T1 and T2 (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.

Member initialization

Non-static data members may be initialized in one of two ways:

1) In the member initializer list of the constructor.
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.

#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
}

Default member initializers are not allowed for bit field members.

(until C++20)

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:

1) As a part of class member access expression, in which the class either has this member or is derived from a class that has this member, including the implicit 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();
};
2) To form a pointer to non-static member.
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
3) (for data members only, not member functions) When used in unevaluated operands.
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
Notes: such uses are allowed via the resolution of CWG issue 613 in N2253, which is treated as a change in C++11 by some compilers (e.g. clang).

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
checks if a type is a standard-layout type
(class template) [edit]
byte offset from the beginning of a standard-layout type to specified member
(function macro) [edit]