C++ Non-Static Reference Member Cant Use Default Assignment Operator

Constructor is a special non-static member function of a class that is used to initialize objects of its class type.

In the definition of a constructor of a class, member initializer list specifies the initializers for direct and virtual base subobjects and non-static data members. ( Not to be confused with std::initializer_list )

[edit]Syntax

Constructors are declared using member function declarators of the following form:

class-nameparameter-list(optional)except-spec(optional)attr(optional) (1)

Where class-name must name the current class (or current instantiation of a class template), or, when declared at namespace scope or in a friend declaration, it must be a qualified class name.

The only specifiers allowed in the decl-specifier-seq of a constructor declaration are friend, inline, and (in particular, no return type is allowed). Note that cv- and ref-qualifiers are not allowed either; const and volatile semantics of an object under construction don't kick in until the most-derived constructor completes.

The body of a function definition of any constructor, before the opening brace of the compound statement, may include the member initializer list, whose syntax is the colon character , followed by the comma-separated list of one or more member-initializers, each of which has the following syntax

class-or-identifierexpression-list(optional) (1)
class-or-identifierbrace-init-list (2) (since C++11)
parameter-pack (3) (since C++11)

1) Initializes the base or member named by class-or-identifier using direct initialization or, if expression-list is empty, value-initialization

2) Initializes the base or member named by class-or-identifier using list-initialization (which becomes value-initialization if the list is empty and aggregate-initialization when initializing an aggregate)

3) Initializes multiple bases using a pack expansion

class-or-identifier - any identifier, class name, or decltype expression that names a non-static data member, a direct or virtual base, or (for delegating constructors) the class itself
expression-list - possibly empty, comma-separated list of the parameters to pass to the constructor of the base or member
braced-init-list - brace-enclosed list of comma-separated initializers and nested braced-init-lists
parameter-pack - name of a variadic template parameter pack

Run this code

struct S {int n; S(int);// constructor declaration S(): n(7){}// constructor definition.// ": n(7)" is the initializer list// ": n(7) {}" is the function body}; S::S(int x): n{x}{}// constructor definition. ": n{x}" is the initializer listint main(){ S s;// calls S::S() S s2(10);// calls S::S(int)}

[edit]Explanation

Constructors have no names and cannot be called directly. They are invoked when initialization takes place, and they are selected according to the rules of initialization. The constructors without specifier are converting constructors. The constructors with a specifier make their type a . Constructors that may be called without any argument are default constructors. Constructors that take another object of the same type as the argument are copy constructors and move constructors.

Before the compound statement that forms the function body of the constructor begins executing, initialization of all direct bases, virtual bases, and non-static data members is finished. Member initializer list is the place where non-default initialization of these objects can be specified. For members that cannot be default-initialized, such as members of reference and const-qualified types, member initializers must be specified. No initialization is performed for anonymous unions or variant members that do not have a member initializer.

The initializers where class-or-identifier names a virtual base class are ignored during execution of constructors of any class that is not the most derived class of the object that's being constructed.

Names that appear in expression-list or brace-init-list are evaluated in scope of the constructor:

Exceptions that are thrown from member initializers may be handled by function-try-block

Member functions (including virtual member functions) can be called from member initializers, but the behavior is undefined if not all direct bases are initialized at that point.

For virtual calls (if the bases are initialized), the same rules apply as the rules for the virtual calls from constructors and destructors: virtual member functions behave as if the dynamic type of *this is the class that's being constructed (dynamic dispatch does not propagate down the inheritance hierarchy) and virtual calls (but not static calls) to pure virtual member functions are undefined behavior.

If a non-static data member has an default member initializer and also appears in a member initializer list, then member initializer list is executed and the default member initializer is ignored:

struct S {int n =42;// default member initializer S(): n(7){}// will set n to 7, not 42};
(since C++11)

Reference members cannot be bound to temporaries in a member initializer list:

Note: same applies to default member initializer

struct A { A(): v(42){}// Errorconstint& v;};
(since C++14)

Delegating constructor

If the name of the class itself appears as class-or-identifier in the member initializer list, then the list must consist of that one member initializer only; such constructor is known as the delegating constructor, and the constructor selected by the only member of the initializer list is the target constructor

In this case, the target constructor is selected by overload resolution and executed first, then the control returns to the delegating constructor and its body is executed.

Delegating constructors cannot be recursive.

Inheriting constructors

See using declaration.

class Foo {public: Foo(char x, int y){} Foo(int y): Foo('a', y){}// Foo(int) delegates to Foo(char,int)};
(since C++11)

[edit]Initialization order

The order of member initializers in the list is irrelevant: the actual order of initialization is as follows:

1) If the constructor is for the most-derived class, virtual base classes are initialized in the order in which they appear in depth-first left-to-right traversal of the base class declarations (left-to-right refers to the appearance in base-specifier lists)

2) Then, direct base classes are initialized in left-to-right order as they appear in this class's base-specifier list

3) Then, non-static data members are initialized in order of declaration in the class definition.

4) Finally, the body of the constructor is executed

(Note: if initialization order was controlled by the appearance in the member initializer lists of different constructors, then the destructor wouldn't be able to ensure that the order of destruction is the reverse of the order of construction)

[edit]Example

[edit]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 1696 C++14 reference members could be initialized to temporaries (whose lifetime would end at the end of ctor) such init is ill-formed

[edit]References

  • C++11 standard (ISO/IEC 14882:2011):
  • 12.1 Constructors [class.ctor]
  • 12.6.2 Initializing bases and members [class.base.init]
  • C++98 standard (ISO/IEC 14882:1998):
  • 12.1 Constructors [class.ctor]
  • 12.6.2 Initializing bases and members [class.base.init]
class X {int a, b, i, j;public:constint& r; X(int i): r(a)// initializes X::r to refer to X::a , b{i}// initializes X::b to the value of the parameter i , i(i)// initializes X::i to the value of the parameter i , j(this->i)// initializes X::j to the value of X::i{}};
#include <fstream>#include <mutex>   struct Base {int n; Base(int n): n(n){}};   struct Class :public Base {unsignedchar x;unsignedchar y;std::mutex m;std::lock_guard<std::mutex> lg;std::fstream f1, f2;   Class (int x ): Base (123), // initialize base class x ( x ), // x (member) is initialized with x (parameter) y {0}, // y initialized to 0 f1{"test.cc", std::ios::app}, // this takes place after m and lg are initialized f2("test.bin", std::ios::binary|std::ios::app), lg ( m ), // lg uses m, which is already initialized m{}// m is initialized before lg even though it appears last here{}// empty compound statement   Class (double a ): Base (0), y ( a+1), x ( y ), // x will be initialized before y, its value here is indeterminate lg ( m ){}// base class constructor does not appear in the list, it is// default-initialized (not the same as if Base() were used, which is value-init)   Class()try// function-try block begins before the function body, which includes init list: Class(0.0), //delegate constructor f1(__func__)//__func__ is available because init-list is a part of constructor{// ...}catch(...){// exception occurred on initialization}};   int main(){ Class c; Class c1(1); Class c2(0.1);}

Inside a class definition, the keyword declares members that are not bound to class instances.

Outside a class definition, it has a different meaning: see storage duration.

[edit]Syntax

staticdata_member (1)
staticmember_function (2)

1) Declares a static data member.

2) Declares a static member function.

[edit]Explanation

Static members of a class are not associated with the objects of the class: they are independent objects with static storage duration or regular functions defined in namespace scope, only once in the program.

The static keyword is only used with the declaration of a static member, inside the class definition, but not with the definition of that static member:

The declaration inside the class body is not a definition and may declare the member to be of incomplete type (other than void), including the type in which the member is declared:

To refer to a static member of class , two forms may be used: qualified name or member access expression or , where is an expression that evaluates to or respectively. When in the same class scope, the qualification is unnecessary:

Static members obey the class member access rules (private, protected, public).

[edit]Static member functions

Static member functions are not associated with any object. When called, they have no this pointer.

Static member functions cannot be virtual, const, or volatile.

The address of a static member function may be stored in a regular pointer to function, but not in a pointer to member function.

[edit]Static data members

Static data members are not associated with any object. They exist even if no objects of the class have been defined. If the static member is declared thread_local(since C++11), there is one such object per thread. Otherwise, there is only one instance of the static data member in the entire program, with static storage duration.

Static data members cannot be mutable.

Static data members of a class in namespace scope have external linkage if the class itself has external linkage (i.e. is not a member of unnamed namespace). Local classes (classes defined inside functions) and unnamed classes, including member classes of unnamed classes, cannot have static data members.

A static data member may be declared . An inline static data member can be defined in the class definition and may specify an initializer. It does not need an out-of-class definition:

struct X {inlinestaticint n =1;};
(since C++17)

[edit]Constant static members

If a static data member of integral or enumeration type is declared const (and not volatile), it can be initialized with an initializer in which every expression is a constant expression, right inside the class definition:

If a static data member of is declared constexpr, it must be initialized with an initializer in which every expression is a constant expression, right inside the class definition:

struct X {constexprstaticint arr[]={1, 2, 3};// OKconstexprstaticstd::complex<double> n ={1,2};// OKconstexprstaticint k;// Error: constexpr static requires an initializer};
(since C++11)

If a const non-inline(since C++17) static data member or a constexpr static data member(since C++11) is odr-used, a definition at namespace scope is still required, but it cannot have an initializer. This definition is deprecated for data members(since C++17).

If a static data member is declared constexpr, it is implicitly inline and does not need to be redeclared at namespace scope. This redeclaration without an initializer (formerly required as shown above) is still permitted, but is deprecated.

(since C++17)

[edit]References

  • C++11 standard (ISO/IEC 14882:2011):
  • 9.4 Static members [class.static]
  • C++98 standard (ISO/IEC 14882:1998):
  • 9.4 Static members [class.static]

[edit]See also

class X {staticint n;};// declaration (uses 'static')int X::n=1;// definition (does not use 'static')
struct Foo;struct S {staticint a[];// declaration, incomplete typestatic Foo x;// declaration, incomplete typestatic S s;// declaration, incomplete type (inside its own definition)};   int S::a[10];// definition, complete typestruct Foo {}; Foo S::x;// definition, complete type S S::s;// definition, complete type
struct X {staticvoid f();// declarationstaticint n;// declaration};   X g(){return X();}// some function returning X   void f(){ X::f();// X::f is a qualified name of static member function g().f();// g().f is member access expression referring to a static member function}   int X::n=7;// definition   void X::f()// definition { n =1;// X::n is accessible as just n in this scope}
struct X {conststaticint n =1;conststaticint m{2};// since C++11conststaticint k;};constint X::k=3;
struct X {staticconstint n =1;staticconstexprint m =4;};constint*p =&X::n, *q =&X::m;// X::n and X::m are odr-usedconstint X::n;// … so a definition is necessaryconstexprint X::m;// … (except for X::m in C++17)

One thought on “C++ Non-Static Reference Member Cant Use Default Assignment Operator

Leave a Reply

Your email address will not be published. Required fields are marked *