$(DDOC $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(SPEC_S Structs and Unions, $(DDOC_BLANKLINE ) $(HEADERNAV_TOC $(HEADERNAV_ITEM intro, Introduction) $(HEADERNAV_SUBITEMS members, Members, $(HEADERNAV_ITEM struct-members, Struct Members) $(HEADERNAV_ITEM union-members, Union Members) $(HEADERNAV_ITEM recursive-types, Recursive Structs and Unions) ) $(HEADERNAV_ITEM struct_layout, Struct Layout) $(HEADERNAV_ITEM POD, Plain Old Data) $(HEADERNAV_ITEM opaque_struct_unions, Opaque Structs and Unions) $(HEADERNAV_SUBITEMS initialization, Initialization, $(HEADERNAV_ITEM default_struct_init, Default Initialization of Structs) $(HEADERNAV_ITEM static_struct_init, Static Initialization of Structs) $(HEADERNAV_ITEM default_union_init, Default Initialization of Unions) $(HEADERNAV_ITEM static_union_init, Static Initialization of Unions) $(HEADERNAV_ITEM dynamic_struct_init, Dynamic Initialization of Structs) $(HEADERNAV_ITEM dynamic_union_init, Dynamic Initialization of Unions) ) $(HEADERNAV_ITEM struct-literal, Struct Literals) $(HEADERNAV_ITEM union-literal, Union Literals) $(HEADERNAV_ITEM anonymous, Anonymous Structs and Unions) $(HEADERNAV_SUBITEMS struct_properties, Struct Properties, $(HEADERNAV_ITEM struct_instance_properties, Struct Instance Properties) $(HEADERNAV_ITEM struct_field_properties, Struct Field Properties) ) $(HEADERNAV_ITEM const-struct, Const, Immutable and Shared Structs) $(HEADERNAV_ITEM UnionConstructor, Union Constructors) $(HEADERNAV_SUBITEMS struct-constructor, Struct Constructors, $(HEADERNAV_ITEM delegating-constructor, Delegating Constructors) $(HEADERNAV_ITEM struct-instantiation, Struct Instantiation) $(HEADERNAV_ITEM constructor-attributes, Constructor Attributes) $(HEADERNAV_ITEM disable_default_construction, Disabling Default Struct Construction) $(HEADERNAV_ITEM field-init, Field initialization inside a constructor) ) $(HEADERNAV_SUBITEMS struct-copy-constructor, Struct Copy Constructors, $(HEADERNAV_ITEM disable-copy, Disabled Copying) $(HEADERNAV_ITEM copy-constructor-attributes, Copy Constructor Attributes) $(HEADERNAV_ITEM implicit-copy-constructors, Implicit Copy Constructors) ) $(HEADERNAV_ITEM struct-postblit, Struct Postblits) $(HEADERNAV_ITEM struct-destructor, Struct Destructors) $(HEADERNAV_ITEM union-field-destruction, Union Field Destruction) $(HEADERNAV_ITEM Invariant, Struct Invariants) $(HEADERNAV_ITEM assign-overload, Identity Assignment Overload) $(HEADERNAV_ITEM alias-this, Alias This) $(HEADERNAV_ITEM nested, Nested Structs) $(HEADERNAV_ITEM unions_and_special_memb_funct, Unions and Special Member Functions) ) $(DDOC_BLANKLINE )

$(LNAME2 intro, Introduction)

$(DDOC_BLANKLINE ) $(P Whereas $(DDLINK spec/class, Classes, classes) are reference types, structs and unions are value types. Structs are simple aggregations of data and their associated operations on that data. ) $(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME StructDeclaration): $(D struct) $(GLINK_LEX Identifier) $(D ;) $(D struct) $(GLINK_LEX Identifier) $(GLINK AggregateBody) $(GLINK2 template, StructTemplateDeclaration) $(I AnonStructDeclaration) $(DDOC_BLANKLINE ) $(GNAME AnonStructDeclaration): $(D struct) $(GLINK AggregateBody) ) $(GRAMMAR $(GNAME UnionDeclaration): $(D union) $(GLINK_LEX Identifier) $(D ;) $(D union) $(GLINK_LEX Identifier) $(GLINK AggregateBody) $(GLINK2 template, UnionTemplateDeclaration) $(I AnonUnionDeclaration) $(DDOC_BLANKLINE ) $(GNAME AnonUnionDeclaration): $(D union) $(GLINK AggregateBody) ) $(GRAMMAR $(GNAME AggregateBody): $(D {) $(GLINK2 module, DeclDefs)$(OPT ) $(D }) ) $(DDOC_BLANKLINE ) $(P The following example declares a struct type with a single integer field:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) i; } $(D_KEYWORD void) main() { S a; a.i = 3; S b = a; $(D_COMMENT // copy a ) a.i++; $(D_KEYWORD assert)(a.i == 4); $(D_KEYWORD assert)(b.i == 3); } ) ) $(DDOC_BLANKLINE ) $(P For local variables, a struct instance is allocated on the stack by default. To allocate on the heap, use new:) $(D_CODE S* p = $(D_KEYWORD new) S; $(D_KEYWORD assert)(p.i == 0); ) $(DDOC_BLANKLINE ) $(P A struct can contain multiple fields which are stored sequentially. Conversely, multiple fields in a union use overlapping storage.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD union) U { $(D_KEYWORD ubyte) i; $(D_KEYWORD char) c; } $(D_KEYWORD void) main() { U u; u.i = 3; $(D_KEYWORD assert)(u.c == '\x03'); u.c++; $(D_KEYWORD assert)(u.i == 4); } ) ) $(DDOC_BLANKLINE )

$(LNAME2 members, Members)

$(DDOC_BLANKLINE )

$(LNAME2 struct-members, Struct Members)

$(DDOC_BLANKLINE ) $(P A struct definition can contain:) $(UL $(LI Fields) $(LI $(DDSUBLINK spec/attribute, static, Static) fields) $(LI $(RELATIVE_LINK2 anonymous, Anonymous Structs and Unions)) $(LI $(DDSUBLINK spec/class, member-functions, member functions) $(UL $(LI static member functions) $(LI $(RELATIVE_LINK2 struct-constructor, Constructors)) $(LI $(RELATIVE_LINK2 struct-destructor, Destructors)) $(LI $(RELATIVE_LINK2 Invariant, Invariants)) $(LI $(DDLINK spec/operatoroverloading, Operator Overloading, Operator Overloading)) ) $(LI $(RELATIVE_LINK2 alias-this, Alias This)) $(LI Other declarations (see $(GLINK2 module, DeclDef))) ) ) $(DDOC_BLANKLINE ) $(P A struct is defined to not have an identity; that is, the implementation is free to make bit copies of the struct as convenient.) $(DDOC_BLANKLINE ) $(BEST_PRACTICE $(OL $(LI Bit fields are supported with the $(LINK2 https://dlang.org/phobos/std_bitmanip.html#bitfields, bitfields) template.) )) $(DDOC_BLANKLINE )

$(LNAME2 union-members, Union Members)

$(DDOC_BLANKLINE ) $(P A union definition can contain:) $(UL $(LI Fields) $(LI $(DDSUBLINK spec/attribute, static, Static) fields) $(LI $(RELATIVE_LINK2 anonymous, Anonymous Structs and Unions)) $(LI $(DDSUBLINK spec/class, member-functions, member functions) $(UL $(LI static member functions) $(LI $(RELATIVE_LINK2 UnionConstructor, Constructors)) $(LI $(DDLINK spec/operatoroverloading, Operator Overloading, Operator Overloading)) ) $(LI $(RELATIVE_LINK2 alias-this, Alias This)) $(LI Other declarations (see $(GLINK2 module, DeclDef))) ) ) $(DDOC_BLANKLINE )

$(LNAME2 recursive-types, Recursive Structs and Unions)

$(DDOC_BLANKLINE ) $(P Structs and unions may not contain a non-static instance of themselves, however, they may contain a pointer to the same type. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { S* ptr; $(D_COMMENT // OK ) S[] slice; $(D_COMMENT // OK ) S s; $(D_COMMENT // error ) S[2] array; $(D_COMMENT // error ) $(D_KEYWORD static) S global; $(D_COMMENT // OK )} ) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 struct_layout, Struct Layout)

$(DDOC_BLANKLINE ) $(P The non-static data members of a struct are called $(I fields). Fields are laid out in lexical order. Fields are aligned according to the $(DDSUBLINK spec/attribute, align, Align Attribute) in effect. Unnamed padding is inserted between fields to align fields. There is no padding between the first field and the start of the object. ) $(DDOC_BLANKLINE ) $(P Structs with no fields of non-zero size (aka $(I Empty Structs)) have a size of one byte.) $(DDOC_BLANKLINE ) $(P Non-static $(RELATIVE_LINK2 nested, function-nested D structs), which access the context of their enclosing scope, have an extra field. ) $(DDOC_BLANKLINE ) $(IMPLEMENTATION_DEFINED $(OL $(LI The default layout of the fields of a struct is an exact match with the $(I associated C compiler).) $(LI g++ and clang++ differ in how empty structs are handled. Both return 1 from sizeof, however, clang++ does not push them onto the parameter stack while g++ does. This is a binary incompatibility between g++ and clang++. dmd follows clang++ behavior for OSX and FreeBSD, and g++ behavior for Linux and other Posix platforms. ) $(LI clang and gcc both return 0 from sizeof for empty structs. Using extern "C++" in clang++ and g++ does not cause them to conform to the behavior of their respective C compilers.) )) $(DDOC_BLANKLINE ) $(UNDEFINED_BEHAVIOR $(OL $(LI The padding data can be accessed, but its contents are undefined.) $(LI Do not pass or return structs with no fields of non-zero size to extern (C) functions. According to C11 6.7.2.1p8 this is undefined behavior.) )) $(DDOC_BLANKLINE ) $(BEST_PRACTICE $(OL $(LI When laying out a struct to match an externally defined layout, use align attributes to describe an exact match. Using a $(DDSUBLINK spec/version, static-assert, Static Assert) to ensure the result is as expected.) $(LI Although the contents of the padding are often zero, do not rely on that.) $(LI Avoid using empty structs when interfacing with C and C++ code.) $(LI Avoid using empty structs as parameters or arguments to variadic functions.) )) $(DDOC_BLANKLINE )

$(LNAME2 POD, Plain Old Data)

$(DDOC_BLANKLINE ) $(P A struct or union is $(I Plain Old Data) (POD) if it meets the following criteria:) $(DDOC_BLANKLINE ) $(OL $(LI it is static, or not nested) $(LI it has no postblits, copy constructors, destructors, or assignment operators) $(LI it has no fields that are themselves non-POD) ) $(DDOC_BLANKLINE ) $(BEST_PRACTICE Structs or unions that interface with C code should be POD.) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 opaque_struct_unions, Opaque Structs and Unions)

$(DDOC_BLANKLINE ) $(P Opaque struct and union declarations do not have an $(GLINK AggregateBody):) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD struct) S; $(D_KEYWORD union) U; $(D_KEYWORD struct) V(T); $(D_KEYWORD union) W(T); ) $(DDOC_BLANKLINE ) $(P The members are completely hidden to the user, and so the only operations on those types are ones that do not require any knowledge of the contents of those types. For example:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S; S.sizeof; $(D_COMMENT // error, size is not known )S s; $(D_COMMENT // error, cannot initialize unknown contents )S* p; $(D_COMMENT // ok, knowledge of members is not necessary )) ) $(DDOC_BLANKLINE ) $(BEST_PRACTICE They can be used to implement the $(LINK2 https://en.wikipedia.org/wiki/Opaque_pointer, PIMPL idiom).) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 initialization, Initialization)

$(DDOC_BLANKLINE )

$(LNAME2 default_struct_init, Default Initialization of Structs)

$(DDOC_BLANKLINE ) $(P Struct fields are by default initialized to whatever the $(GLINK2 declaration, Initializer) for the field is, and if none is supplied, to the default initializer for the field's type. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) a = 4; $(D_KEYWORD int) b; } S x; $(D_COMMENT // x.a is set to 4, x.b to 0 )) ) $(DDOC_BLANKLINE ) $(P The default initializers are evaluated at compile time.) $(DDOC_BLANKLINE )

$(LNAME2 static_struct_init, Static Initialization of Structs)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME StructInitializer): $(D {) $(I StructMemberInitializers)$(OPT ) $(D }) $(DDOC_BLANKLINE ) $(GNAME StructMemberInitializers): $(I StructMemberInitializer) $(I StructMemberInitializer) $(D ,) $(I StructMemberInitializer) $(D ,) $(GSELF StructMemberInitializers) $(DDOC_BLANKLINE ) $(GNAME StructMemberInitializer): $(GLINK2 declaration, NonVoidInitializer) $(GLINK_LEX Identifier) $(D :) $(GLINK2 declaration, NonVoidInitializer) ) $(DDOC_BLANKLINE ) $(P If a $(I StructInitializer) is supplied, the fields are initialized by the $(I StructMemberInitializer) syntax. $(I StructMemberInitializers) with the $(I Identifier : NonVoidInitializer) syntax may be appear in any order, where $(I Identifier) is the field identifier. $(I StructMemberInitializer)s with the $(GLINK2 declaration, NonVoidInitializer) syntax appear in the lexical order of the fields in the $(GLINK StructDeclaration). ) $(DDOC_BLANKLINE ) $(P Fields not specified in the $(I StructInitializer) are default initialized.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) a, b, c, d = 7; } S r; $(D_COMMENT // r.a = 0, r.b = 0, r.c = 0, r.d = 7 )S s = { a:1, b:2 }; $(D_COMMENT // s.a = 1, s.b = 2, s.c = 0, s.d = 7 )S t = { c:4, b:5, a:2, d:5 }; $(D_COMMENT // t.a = 2, t.b = 5, t.c = 4, t.d = 5 )S u = { 1, 2 }; $(D_COMMENT // u.a = 1, u.b = 2, u.c = 0, u.d = 7 )S v = { 1, d:3 }; $(D_COMMENT // v.a = 1, v.b = 0, v.c = 0, v.d = 3 )S w = { b:1, 3 }; $(D_COMMENT // w.a = 0, w.b = 1, w.c = 3, w.d = 7 )) ) $(DDOC_BLANKLINE ) $(P Initializing a field more than once is an error:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE S x = { 1, a:2 }; $(D_COMMENT // error: duplicate initializer for field `a` )) ) $(DDOC_BLANKLINE )

$(LNAME2 default_union_init, Default Initialization of Unions)

$(DDOC_BLANKLINE ) $(P Unions are by default initialized to whatever the $(GLINK2 declaration, Initializer) for the first field is, and if none is supplied, to the default initializer for the first field's type. If the union is larger than the first field, the remaining bits are set to 0.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD union) U { $(D_KEYWORD int) a = 4; $(D_KEYWORD long) b; } U x; $(D_COMMENT // x.a is set to 4, x.b to an implementation-defined value )) ) $(DDOC_BLANKLINE ) $(P It is an error to supply initializers for members other than the first one.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD union) V { $(D_KEYWORD int) a; $(D_KEYWORD long) b = 4; } $(D_COMMENT // error: union field `b` with default initialization `4` must be before field `a` )$(D_KEYWORD union) W { $(D_KEYWORD int) a = 4; $(D_KEYWORD long) b = 5; } $(D_COMMENT // error: overlapping default initialization for `a` and `b` )) ) $(DDOC_BLANKLINE ) $(P The default initializer is evaluated at compile time.) $(DDOC_BLANKLINE ) $(IMPLEMENTATION_DEFINED The values the fields other than the default initialized field are set to.) $(DDOC_BLANKLINE )

$(LNAME2 static_union_init, Static Initialization of Unions)

$(DDOC_BLANKLINE ) $(P Unions are initialized similarly to structs, except that only one member initializer is allowed.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD union) U { $(D_KEYWORD int) a; $(D_KEYWORD double) b; } U u = { 2 }; $(D_COMMENT // u.a = 2 )U v = { b : 5.0 }; $(D_COMMENT // v.b = 5.0 )) ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE U w = { 2, 3 }; $(D_COMMENT // error: overlapping initialization for field `a` and `b` )) ) $(DDOC_BLANKLINE ) $(P If the union is larger than the initialized field, the remaining bits are set to 0.) $(DDOC_BLANKLINE ) $(IMPLEMENTATION_DEFINED The values the fields other than the initialized field are set to.) $(DDOC_BLANKLINE )

$(LNAME2 dynamic_struct_init, Dynamic Initialization of Structs)

$(DDOC_BLANKLINE ) $(P The $(RELATIVE_LINK2 static_struct_init, static initializer syntax) can also be used to initialize non-static variables. The initializer need not be evaluable at compile time.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) a, b, c, d = 7; } $(D_KEYWORD void) test($(D_KEYWORD int) i) { S q = { 1, b:i }; $(D_COMMENT // q.a = 1, q.b = i, q.c = 0, q.d = 7 )} ) ) $(DDOC_BLANKLINE ) $(P Structs can be dynamically initialized from another value of the same type:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) a; } S t; $(D_COMMENT // default initialized )t.a = 3; S s = t; $(D_COMMENT // s.a is set to 3 )) ) $(DDOC_BLANKLINE ) $(P If the struct has a $(RELATIVE_LINK2 struct-constructor, constructor), and the struct is initialized with a value that is of a different type, then the constructor is called:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) a; $(D_KEYWORD this)($(D_KEYWORD int) v) { $(D_KEYWORD this).a = v; } } S s = 3; $(D_COMMENT // sets s.a to 3 using S's constructor )) ) $(DDOC_BLANKLINE ) $(P If the struct does not have a constructor but $(DDSUBLINK spec/operatoroverloading, FunctionCall, opCall) is overridden for the struct, and the struct is initialized with a value that is of a different type, then the $(D opCall) operator is called:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) a; $(D_KEYWORD static) S opCall($(D_KEYWORD int) v) { S s; s.a = v; $(D_KEYWORD return) s; } $(D_KEYWORD static) S opCall(S v) { $(D_KEYWORD assert)(0); } } S s = 3; $(D_COMMENT // sets s.a to 3 using S.opCall$(LPAREN)int$(RPAREN ) )S t = s; $(D_COMMENT // sets t.a to 3, S.opCall$(LPAREN)S$(RPAREN ) is not called )) ) $(DDOC_BLANKLINE )

$(LNAME2 dynamic_union_init, Dynamic Initialization of Unions)

$(DDOC_BLANKLINE ) $(P The $(RELATIVE_LINK2 static_union_init, static initializer syntax) can also be used to initialize non-static variables. The initializer need not be evaluable at compile time.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD union) U { $(D_KEYWORD int) a; $(D_KEYWORD double) b; } $(D_KEYWORD void) test($(D_KEYWORD int) i) { U u = { a : i }; $(D_COMMENT // u.a = i ) U v = { b : 5.0 }; $(D_COMMENT // v.b = 5.0 )} ) ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 StructLiteral, struct-literal, Struct Literals)

$(DDOC_BLANKLINE ) $(P A struct literal consists of the name of the struct followed by a parenthesized argument list:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) x; $(D_KEYWORD float) y; } $(D_KEYWORD int) foo(S s) { $(D_KEYWORD return) s.x; } foo( S(1, 2) ); $(D_COMMENT // set field x to 1, field y to 2 )) ) $(DDOC_BLANKLINE ) $(P Struct literals are syntactically like function calls. If a struct has a member function named $(CODE opCall), then struct literals for that struct are not possible. See also $(DDSUBLINK spec/operatoroverloading, FunctionCall, opCall operator overloading) for the issue workaround.) $(P It is an error if there are more arguments than fields of the struct. If there are fewer arguments than fields, the remaining fields are initialized with their respective default initializers.) $(P If there is a union field in the struct, only the first member of the union can be initialized inside a struct literal. This matches the behaviour for union literals. ) $(DDOC_BLANKLINE )

$(LNAME2 union-literal, Union Literals)

$(DDOC_BLANKLINE ) $(P A union literal is like a struct literal, but only the first field can be initialized with an initializer expression. Any field larger than the first will have the remainder of its memory initialized to zero. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD union) U { $(D_KEYWORD byte) a; $(D_KEYWORD char)[2] b; } U u = U(2); $(D_KEYWORD assert)(u.a == 2); $(D_KEYWORD assert)(u.b == [2, 0]); ) ) $(DDOC_BLANKLINE )

$(LNAME2 anonymous, Anonymous Structs and Unions)

$(DDOC_BLANKLINE ) $(P An anonymous struct or union can be declared as a member of a parent class, struct or union by omitting the identifier after struct or union. An anonymous struct declares sequentially stored fields in the parent type. An anonymous union declares overlapping fields in the parent type.) $(DDOC_BLANKLINE ) $(P An anonymous union is useful inside a class or struct to share memory for fields, without having to name a parent field with a separate union type.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) a; $(D_KEYWORD union) { $(D_KEYWORD byte) b; $(D_KEYWORD char) c; } } S s = S(1, 2); $(D_KEYWORD assert)(s.a == 1); $(D_KEYWORD assert)(s.b == 2); $(D_KEYWORD assert)(s.c == 2); $(D_COMMENT // overlaps with `b` )) ) $(DDOC_BLANKLINE ) $(P Conversely, an anonymous struct is useful inside a union to declare multiple fields that are stored sequentially.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD union) U { $(D_KEYWORD int) a; $(D_KEYWORD struct) { $(D_KEYWORD uint) b; $(D_KEYWORD bool) c; } } U u = U(1); $(D_KEYWORD assert)(u.a == 1); $(D_KEYWORD assert)(u.b == 1); $(D_COMMENT // overlaps with `a` )$(D_KEYWORD assert)(u.c == $(D_KEYWORD false)); $(D_COMMENT // no overlap )) ) $(DDOC_BLANKLINE )

$(LNAME2 struct_properties, Struct Properties)

$(DDOC_BLANKLINE ) $(TABLE2 Struct Properties, Name, Description $(TROW $(D .sizeof), Size in bytes of struct) $(TROW $(D .alignof), Size boundary struct needs to be aligned on) ) $(DDOC_BLANKLINE )

$(LNAME2 struct_instance_properties, Struct Instance Properties)

$(DDOC_BLANKLINE ) $(TABLE2 Struct Instance Properties, Name, Description $(TROW $(D .tupleof), An $(DDSUBLINK spec/template, homogeneous_sequences, lvalue sequence) of all struct fields - see $(DDSUBLINK spec/class, class_properties, Class Properties) for a class-based example.) ) $(DDOC_BLANKLINE )

$(LNAME2 struct_field_properties, Struct Field Properties)

$(DDOC_BLANKLINE ) $(TABLE2 Struct Field Properties, Name, Description $(TROW $(D .offsetof), Offset in bytes of field from beginning of struct) ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 ConstStruct, const-struct, Const, Immutable and Shared Structs)

$(DDOC_BLANKLINE ) $(P A struct declaration can have a storage class of $(CODE const), $(CODE immutable) or $(CODE shared). It has an equivalent effect as declaring each member of the struct as $(CODE const), $(CODE immutable) or $(CODE shared). ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD const) $(D_KEYWORD struct) S { $(D_KEYWORD int) a; $(D_KEYWORD int) b = 2; } $(D_KEYWORD void) main() { S s = S(3); $(D_COMMENT // initializes s.a to 3 ) S t; $(D_COMMENT // initializes t.a to 0 ) t = s; $(D_COMMENT // error, t.a and t.b are const, so cannot modify them. ) t.a = 4; $(D_COMMENT // error, t.a is const )} ) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 UnionConstructor, Union Constructors)

$(DDOC_BLANKLINE ) $(P Unions are constructed in the same way as structs.) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 Struct-Constructor, struct-constructor, Struct Constructors)

$(DDOC_BLANKLINE ) $(P Struct constructors are used to initialize an instance of a struct when a more complex construction is needed than is allowed by $(RELATIVE_LINK2 static_struct_init, static initialization) or a $(RELATIVE_LINK2 struct-literal, struct literal). ) $(DDOC_BLANKLINE ) $(P Constructors are defined with a function name of this and have no return value. The grammar is the same as for the class $(GLINK2 class, Constructor). ) $(DDOC_BLANKLINE ) $(P A struct constructor is called by the name of the struct followed by $(GLINK2 function, Parameters). ) $(P If the $(GLINK2 function, ParameterList) is empty, the struct instance is default initialized.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) x, y = 4, z = 6; $(D_KEYWORD this)($(D_KEYWORD int) a, $(D_KEYWORD int) b) { x = a; y = b; } } $(D_KEYWORD void) main() { S a = S(4, 5); $(D_COMMENT // calls S.this$(LPAREN)4, 5$(RPAREN ): a.x = 4, a.y = 5, a.z = 6 ) S b = S(); $(D_COMMENT // default initialized: b.x = 0, b.y = 4, b.z = 6 ) S c = S(1); $(D_COMMENT // error, matching this$(LPAREN)int$(RPAREN ) not found )} ) ) $(DDOC_BLANKLINE ) $(P A $(I default constructor) (i.e. one with an empty $(GLINK2 function, ParameterList)) is not allowed.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) x; $(D_KEYWORD this)() { } $(D_COMMENT // error, struct default constructor not allowed )} ) ) $(DDOC_BLANKLINE )

$(LNAME2 delegating-constructor, Delegating Constructors)

$(DDOC_BLANKLINE ) $(P A constructor can call another constructor for the same struct in order to share common initializations. This is called a $(I delegating constructor): ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) j = 1; $(D_KEYWORD long) k = 2; $(D_KEYWORD this)($(D_KEYWORD long) k) { $(D_KEYWORD this).k = k; } $(D_KEYWORD this)($(D_KEYWORD int) i) { $(D_COMMENT // At this point: j=1, k=2 ) $(CODE_HIGHLIGHT $(D_KEYWORD this))(6); $(D_COMMENT // delegating constructor call ) $(D_COMMENT // At this point: j=1, k=6 ) j = i; $(D_COMMENT // At this point: j=i, k=6 ) } } ) ) $(DDOC_BLANKLINE ) $(P The following restrictions apply:) $(DDOC_BLANKLINE ) $(OL $(LI If a constructor's code contains a delegating constructor call, all possible execution paths through the constructor must make exactly one delegating constructor call: $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) a; $(D_KEYWORD this)($(D_KEYWORD int) i) { } $(D_KEYWORD this)($(D_KEYWORD char) c) { c || $(D_KEYWORD this)(1); $(D_COMMENT // error, not on all paths ) } $(D_KEYWORD this)($(D_KEYWORD wchar) w) { (w) ? $(D_KEYWORD this)(1) : $(D_KEYWORD this)('c'); $(D_COMMENT // ok ) } $(D_KEYWORD this)($(D_KEYWORD byte) b) { $(D_KEYWORD foreach) (i; 0 .. b) { $(D_KEYWORD this)(1); $(D_COMMENT // error, inside loop ) } } } ) ) ) $(DDOC_BLANKLINE ) $(LI It is illegal to refer to $(D this) implicitly or explicitly prior to making a delegating constructor call.) $(DDOC_BLANKLINE ) $(LI Once the delegating constructor returns, all fields are considered constructed.) $(DDOC_BLANKLINE ) $(LI Delegating constructor calls cannot appear after labels.) ) $(DDOC_BLANKLINE ) $(P See also: $(DDSUBLINK spec/class, delegating-constructors, delegating class constructors).) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 struct-instantiation, Struct Instantiation)

$(DDOC_BLANKLINE ) $(P When an instance of a struct is created, the following steps happen:) $(DDOC_BLANKLINE ) $(OL $(LI The raw data is statically initialized using the values provided in the struct definition. This operation is equivalent to doing a memory copy of a static version of the object onto the newly allocated one. ) $(DDOC_BLANKLINE ) $(LI If there is a constructor defined for the struct, the constructor matching the argument list is called. ) $(DDOC_BLANKLINE ) $(LI If struct invariant checking is turned on, the struct invariant is called at the end of the constructor. ) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 constructor-attributes, Constructor Attributes)

$(DDOC_BLANKLINE ) $(P A constructor qualifier (const, immutable or shared) constructs the object instance with that specific qualifier. ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S1 { $(D_KEYWORD int)[] a; $(D_KEYWORD this)($(D_KEYWORD int) n) { a = $(D_KEYWORD new) $(D_KEYWORD int)[](n); } } $(D_KEYWORD struct) S2 { $(D_KEYWORD int)[] a; $(D_KEYWORD this)($(D_KEYWORD int) n) $(D_KEYWORD immutable) { a = $(D_KEYWORD new) $(D_KEYWORD int)[](n); } } $(D_KEYWORD void) main() { $(D_COMMENT // Mutable constructor creates mutable object. ) S1 m1 = S1(1); $(D_COMMENT // Constructed mutable object is implicitly convertible to const. ) $(D_KEYWORD const) S1 c1 = S1(1); $(D_COMMENT // Constructed mutable object is not implicitly convertible to immutable. ) $(D_KEYWORD immutable) i1 = S1(1); $(D_COMMENT // error ) $(D_COMMENT // Mutable constructor cannot construct immutable object. ) $(D_KEYWORD auto) x1 = $(D_KEYWORD immutable) S1(1); $(D_COMMENT // error ) $(D_COMMENT // Immutable constructor creates immutable object. ) $(D_KEYWORD immutable) i2 = $(D_KEYWORD immutable) S2(1); $(D_COMMENT // Immutable constructor cannot construct mutable object. ) $(D_KEYWORD auto) x2 = S2(1); $(D_COMMENT // error ) $(D_COMMENT // Constructed immutable object is not implicitly convertible to mutable. ) S2 m2 = $(D_KEYWORD immutable) S2(1); $(D_COMMENT // error ) $(D_COMMENT // Constructed immutable object is implicitly convertible to const. ) $(D_KEYWORD const) S2 c2 = $(D_KEYWORD immutable) S2(1); } ) ) $(DDOC_BLANKLINE ) $(P Constructors can be overloaded with different attributes.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD this)($(D_KEYWORD int)); $(D_COMMENT // non-shared mutable constructor ) $(D_KEYWORD this)($(D_KEYWORD int)) $(D_KEYWORD shared); $(D_COMMENT // shared mutable constructor ) $(D_KEYWORD this)($(D_KEYWORD int)) $(D_KEYWORD immutable); $(D_COMMENT // immutable constructor )} $(D_KEYWORD void) fun() { S m = S(1); $(D_KEYWORD shared) s = $(D_KEYWORD shared) S(2); $(D_KEYWORD immutable) i = $(D_KEYWORD immutable) S(3); } ) ) $(DDOC_BLANKLINE )

$(LNAME2 pure-constructors, Pure Constructors)

$(DDOC_BLANKLINE ) $(P If the constructor can create a unique object (i.e. if it is pure), the object is implicitly convertible to any qualifiers. ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD this)($(D_KEYWORD int)) $(D_KEYWORD pure); $(D_COMMENT // Based on the definition, this creates a mutable object. But the ) $(D_COMMENT // created object cannot contain any mutable global data. ) $(D_COMMENT // Therefore the created object is unique. ) $(D_KEYWORD this)($(D_KEYWORD int)[] arr) $(D_KEYWORD immutable) $(D_KEYWORD pure); $(D_COMMENT // Based on the definition, this creates an immutable object. But ) $(D_COMMENT // the argument int[] never appears in the created object so it ) $(D_COMMENT // isn't implicitly convertible to immutable. Also, it cannot store ) $(D_COMMENT // any immutable global data. ) $(D_COMMENT // Therefore the created object is unique. )} $(D_KEYWORD void) fun() { $(D_KEYWORD immutable) i = $(D_KEYWORD immutable) S(1); $(D_COMMENT // this$(LPAREN)int$(RPAREN ) pure is called ) $(D_KEYWORD shared) s = $(D_KEYWORD shared) S(1); $(D_COMMENT // this$(LPAREN)int$(RPAREN ) pure is called ) S m = S([1,2,3]); $(D_COMMENT // this$(LPAREN)int[]$(RPAREN ) immutable pure is called )} ) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 disable_default_construction, Disabling Default Struct Construction)

$(DDOC_BLANKLINE ) $(P If a struct constructor is annotated with $(D @disable) and has an empty $(GLINK2 function, ParameterList), the struct has disabled default construction. The only way it can be constructed is via a call to another constructor with a non-empty $(I ParameterList). ) $(DDOC_BLANKLINE ) $(P A struct with a disabled default constructor, and no other constructors, cannot be instantiated other than via a $(GLINK2 declaration, VoidInitializer).) $(DDOC_BLANKLINE ) $(P A disabled default constructor may not have a $(GLINK2 function, FunctionBody).) $(DDOC_BLANKLINE ) $(P If any fields have disabled default construction, struct default construction is also disabled.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) x; $(D_COMMENT // Disables default construction ) @disable $(D_KEYWORD this)(); $(D_KEYWORD this)($(D_KEYWORD int) v) { x = v; } } $(D_KEYWORD struct) T { $(D_KEYWORD int) y; S s; } $(D_KEYWORD void) main() { S s; $(D_COMMENT // error: default construction is disabled ) S t = S(); $(D_COMMENT // error: also disabled ) S u = S(1); $(D_COMMENT // constructed by calling `S.this$(LPAREN)1$(RPAREN )` ) S v = $(D_KEYWORD void); $(D_COMMENT // not initialized, but allowed ) S w = { 1 }; $(D_COMMENT // error: cannot use { } since constructor exists ) S[3] a; $(D_COMMENT // error: default construction is disabled ) S[3] b = [S(1), S(20), S(-2)]; $(D_COMMENT // ok ) T t; $(D_COMMENT // error: default construction is disabled )} ) ) $(DDOC_BLANKLINE ) $(BEST_PRACTICE Disabling default construction is useful when the default value, such as null, is not acceptable.) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 field-init, Field initialization inside a constructor)

$(DDOC_BLANKLINE ) $(P In a constructor body, if a delegating constructor is called, all field assignments are considered assignments. Otherwise, the first instance of field assignment is its initialization, and assignments of the form field = expression are treated as equivalent to typeof(field)(expression). The values of fields may be read before initialization or construction with a delegating constructor. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) num; $(D_KEYWORD int) ber; $(D_KEYWORD this)($(D_KEYWORD int) i) { num = i + 1; $(D_COMMENT // initialization ) num = i + 2; $(D_COMMENT // assignment ) ber = ber + 1; $(D_COMMENT // ok to read before initialization ) } $(D_KEYWORD this)($(D_KEYWORD int) i, $(D_KEYWORD int) j) { $(D_KEYWORD this)(i); num = i + 1; $(D_COMMENT // assignment ) } } ) ) $(DDOC_BLANKLINE ) $(P If the field type has an $(DDSUBLINK spec/operatoroverloading, assignment, opAssign) method, it will not be used for initialization.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) A { $(D_KEYWORD this)($(D_KEYWORD int) n) {} $(D_KEYWORD void) opAssign(A rhs) {} } $(D_KEYWORD struct) S { A val; $(D_KEYWORD this)($(D_KEYWORD int) i) { val = A(i); $(D_COMMENT // val is initialized to the value of A$(LPAREN)i$(RPAREN ) ) val = A(2); $(D_COMMENT // rewritten to val.opAssign$(LPAREN)A$(LPAREN)2$(RPAREN )$(RPAREN ) ) } } ) ) $(DDOC_BLANKLINE ) $(P If the field type is not mutable, multiple initialization will be rejected.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD immutable) $(D_KEYWORD int) num; $(D_KEYWORD this)($(D_KEYWORD int)) { num = 1; $(D_COMMENT // OK ) num = 2; $(D_COMMENT // Error: assignment to immutable ) } } ) ) $(DDOC_BLANKLINE ) $(P If the field is initialized on one path, it must be initialized on all paths.) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD immutable) $(D_KEYWORD int) num; $(D_KEYWORD immutable) $(D_KEYWORD int) ber; $(D_KEYWORD this)($(D_KEYWORD int) i) { $(D_KEYWORD if) (i) num = 3; $(D_COMMENT // initialization ) $(D_KEYWORD else) num = 4; $(D_COMMENT // initialization ) } $(D_KEYWORD this)($(D_KEYWORD long) j) { j ? (num = 3) : (num = 4); $(D_COMMENT // ok ) j || (ber = 3); $(D_COMMENT // Error: initialized on only one path ) j && (ber = 3); $(D_COMMENT // Error: initialized on only one path ) } } ) ) $(DDOC_BLANKLINE ) $(P A field initialization may not appear in a loop or after a label.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD immutable) $(D_KEYWORD int) num; $(D_KEYWORD immutable) string str; $(D_KEYWORD this)($(D_KEYWORD int) j) { $(D_KEYWORD foreach) (i; 0..j) { num = 1; $(D_COMMENT // Error: field initialization not allowed in loops ) } size_t i = 0; Label: str = $(D_STRING "hello"); $(D_COMMENT // Error: field initialization not allowed after labels ) $(D_KEYWORD if) (i++ < 2) $(D_KEYWORD goto) Label; } $(D_KEYWORD this)($(D_KEYWORD int) j, $(D_KEYWORD int) k) { $(D_KEYWORD switch) (j) { $(D_KEYWORD case) 1: ++j; $(D_KEYWORD break); $(D_KEYWORD default): $(D_KEYWORD break); } num = j; $(D_COMMENT // Error: `case` and `default` are also labels ) } } ) ) $(DDOC_BLANKLINE ) $(P If a field's type has disabled default construction, then it must be initialized in the constructor.) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) y; @disable $(D_KEYWORD this)(); } $(D_KEYWORD struct) T { S s; $(D_KEYWORD this)(S t) { s = t; } $(D_COMMENT // ok ) $(D_KEYWORD this)($(D_KEYWORD int) i) { $(D_KEYWORD this)('c'); } $(D_COMMENT // ok ) $(D_KEYWORD this)($(D_KEYWORD char)) { } $(D_COMMENT // Error: s not initialized )} ) ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 StructCopyConstructor, struct-copy-constructor, Struct Copy Constructors)

$(DDOC_BLANKLINE ) $(P Copy constructors are used to initialize a struct instance from another instance of the same type. A struct that defines a copy constructor is not $(RELATIVE_LINK2 POD, POD).) $(DDOC_BLANKLINE ) $(P A constructor declaration is a copy constructor declaration if it meets the following requirements:) $(DDOC_BLANKLINE ) $(UL $(LI It takes exactly one parameter without a $(DDSUBLINK spec/function, function-default-args, default argument), followed by any number of parameters with default arguments.) $(DDOC_BLANKLINE ) $(LI Its first parameter is a $(DDSUBLINK spec/function, ref-params, ref parameter).) $(DDOC_BLANKLINE ) $(LI The type of its first parameter is the same type as $(DDSUBLINK spec/type, typeof, typeof(this)), optionally with one or more $(DDLINK spec/const3, Type Qualifiers, type qualifiers) applied to it.) $(DDOC_BLANKLINE ) $(LI It is not a $(DDSUBLINK spec/template, template_ctors, template constructor declaration).) ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD struct) A { $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) A rhs) {} $(D_COMMENT // copy constructor ) $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) $(D_KEYWORD const) A rhs, $(D_KEYWORD int) b = 7) {} $(D_COMMENT // copy constructor with default parameter )} ) $(DDOC_BLANKLINE ) $(P The copy constructor is type checked as a normal constructor.) $(DDOC_BLANKLINE ) $(P If a copy constructor is defined, implicit calls to it will be inserted in the following situations:) $(DDOC_BLANKLINE ) $(OL $(LI When a variable is explicitly initialized:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) A { $(D_KEYWORD int)[] arr; $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) A rhs) { arr = rhs.arr.dup; } } $(D_KEYWORD void) main() { A a; a.arr = [1, 2]; A b = a; $(D_COMMENT // copy constructor gets called ) b.arr[] += 1; $(D_KEYWORD assert)(a.arr == [1, 2]); $(D_COMMENT // a is unchanged ) $(D_KEYWORD assert)(b.arr == [2, 3]); } ) ) $(DDOC_BLANKLINE ) $(LI When a parameter is passed by value to a function:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) A { $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) A another) {} } $(D_KEYWORD void) fun(A a) {} $(D_KEYWORD void) main() { A a; fun(a); $(D_COMMENT // copy constructor gets called )} ) ) $(DDOC_BLANKLINE ) $(LI When a parameter is returned by value from a function and Named Returned Value Optimization (NRVO) cannot be performed:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) A { $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) A another) {} } A fun() { A a; $(D_KEYWORD return) a; $(D_COMMENT // NRVO, no copy constructor call )} A a; A gun() { $(D_KEYWORD return) a; $(D_COMMENT // cannot perform NRVO, rewrite to: return $(LPAREN)A __tmp; __tmp.copyCtor$(LPAREN)a$(RPAREN )$(RPAREN ); )} $(D_KEYWORD void) main() { A a = fun(); A b = gun(); } ) ) ) $(DDOC_BLANKLINE )

$(LNAME2 disable-copy, Disabled Copying)

$(DDOC_BLANKLINE ) $(P When a copy constructor is defined for a struct (or marked @disable), the compiler no longer implicitly generates default copy/blitting constructors for that struct: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) A { $(D_KEYWORD int)[] a; $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) A rhs) {} } $(D_KEYWORD void) fun($(D_KEYWORD immutable) A) {} $(D_KEYWORD void) main() { $(D_KEYWORD immutable) A a; fun(a); $(D_COMMENT // error: copy constructor cannot be called with types $(LPAREN)immutable$(RPAREN ) immutable )} ) ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) A { @disable $(D_KEYWORD this)($(D_KEYWORD ref) A); } $(D_KEYWORD void) main() { A a; A b = a; $(D_COMMENT // error: copy constructor is disabled )} ) ) $(DDOC_BLANKLINE ) $(P If a union U has fields that define a copy constructor, whenever an object of type U is initialized by copy, an error will be issued. The same rule applies to overlapped fields (anonymous unions).) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD this)($(D_KEYWORD ref) S); } $(D_KEYWORD union) U { S s; } $(D_KEYWORD void) main() { U a; U b = a; $(D_COMMENT // error, could not generate copy constructor for U )} ) ) $(DDOC_BLANKLINE )

$(LNAME2 copy-constructor-attributes, Copy Constructor Attributes)

$(DDOC_BLANKLINE ) $(P The copy constructor can be overloaded with different qualifiers applied to the parameter (copying from a qualified source) or to the copy constructor itself (copying to a qualified destination): ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) A { $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) A another) {} $(D_COMMENT // 1 - mutable source, mutable destination ) $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) $(D_KEYWORD immutable) A another) {} $(D_COMMENT // 2 - immutable source, mutable destination ) $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) A another) $(D_KEYWORD immutable) {} $(D_COMMENT // 3 - mutable source, immutable destination ) $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) $(D_KEYWORD immutable) A another) $(D_KEYWORD immutable) {} $(D_COMMENT // 4 - immutable source, immutable destination )} $(D_KEYWORD void) main() { A a; $(D_KEYWORD immutable) A ia; A a2 = a; $(D_COMMENT // calls 1 ) A a3 = ia; $(D_COMMENT // calls 2 ) $(D_KEYWORD immutable) A a4 = a; $(D_COMMENT // calls 3 ) $(D_KEYWORD immutable) A a5 = ia; $(D_COMMENT // calls 4 )} ) ) $(DDOC_BLANKLINE ) $(P The inout qualifier may be applied to the copy constructor parameter in order to specify that mutable, const, or immutable types are treated the same: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) A { $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) $(D_KEYWORD inout) A rhs) $(D_KEYWORD immutable) {} } $(D_KEYWORD void) main() { A r1; $(D_KEYWORD const)(A) r2; $(D_KEYWORD immutable)(A) r3; $(D_COMMENT // All call the same copy constructor because `inout` acts like a wildcard ) $(D_KEYWORD immutable)(A) a = r1; $(D_KEYWORD immutable)(A) b = r2; $(D_KEYWORD immutable)(A) c = r3; } ) ) $(DDOC_BLANKLINE )

$(LNAME2 implicit-copy-constructors, Implicit Copy Constructors)

$(DDOC_BLANKLINE ) $(P A copy constructor is generated implicitly by the compiler for a struct S if all of the following conditions are met:) $(DDOC_BLANKLINE ) $(OL $(LI S does not explicitly declare any copy constructors;) $(LI S defines at least one direct member that has a copy constructor, and that member is not overlapped (by means of union) with any other member.) ) $(DDOC_BLANKLINE ) $(P If the restrictions above are met, the following copy constructor is generated:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD this)($(D_KEYWORD ref) $(D_KEYWORD return) $(D_KEYWORD scope) $(D_KEYWORD inout)(S) src) $(D_KEYWORD inout) { $(D_KEYWORD foreach) (i, $(D_KEYWORD ref) $(D_KEYWORD inout) field; src.tupleof) $(D_KEYWORD this).tupleof[i] = field; } ) $(DDOC_BLANKLINE ) $(P If the generated copy constructor fails to type check, it will receive the @disable attribute.) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 StructPostblit, struct-postblit, Struct Postblits)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME Postblit): $(D this $(LPAREN) this $(RPAREN )) $(GLINK2 function, MemberFunctionAttributes)$(OPT ) $(GLINK2 function, FunctionBody) ) $(DDOC_BLANKLINE ) $(P $(RED Warning): The postblit is considered legacy and is not recommended for new code. Code should use $(RELATIVE_LINK2 struct-copy-constructor, copy constructors) defined in the previous section. For backward compatibility reasons, a struct that explicitly defines both a copy constructor and a postblit will only use the postblit for implicit copying. However, if the postblit is disabled, the copy constructor will be used. If a struct defines a copy constructor (user-defined or generated) and has fields that define postblits, a deprecation will be issued, informing that the postblit will have priority over the copy constructor.) $(DDOC_BLANKLINE ) $(P $(I Copy construction) is defined as initializing a struct instance from another instance of the same type. Copy construction is divided into two parts:) $(DDOC_BLANKLINE ) $(OL $(LI blitting the fields, i.e. copying the bits) $(LI running $(I postblit) on the result) ) $(DDOC_BLANKLINE ) $(P The first part is done automatically by the language, the second part is done if a postblit function is defined for the struct. The postblit has access only to the destination struct object, not the source. Its job is to $(SINGLEQUOTE fix up) the destination as necessary, such as making copies of referenced data, incrementing reference counts, etc. For example: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int)[] a; $(D_COMMENT // array is privately owned by this instance ) $(D_KEYWORD this)($(D_KEYWORD this)) { a = a.dup; } } ) ) $(DDOC_BLANKLINE ) $(P Disabling struct postblit makes the object not copyable. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) T { @disable $(D_KEYWORD this)($(D_KEYWORD this)); $(D_COMMENT // disabling makes T not copyable )} $(D_KEYWORD struct) S { T t; $(D_COMMENT // uncopyable member makes S also not copyable )} $(D_KEYWORD void) main() { S s; S t = s; $(D_COMMENT // error, S is not copyable )} ) ) $(DDOC_BLANKLINE ) $(P Depending on the struct layout, the compiler may generate the following internal postblit functions:) $(DDOC_BLANKLINE ) $(OL $(LI void __postblit(). The compiler assigns this name to the explicitly defined postblit this(this) so that it can be treated exactly as a normal function. Note that if a struct defines a postblit, it cannot define a function named __postblit - no matter the signature - as this would result in a compilation error due to the name conflict.) $(LI void __fieldPostblit(). If a struct X has at least one struct member that in turn defines (explicitly or implicitly) a postblit, then a field postblit is generated for X that calls all the underlying postblits of the struct fields in declaration order.) $(LI void __aggrPostblit(). If a struct has an explicitly defined postblit and at least 1 struct member that has a postblit (explicit or implicit) an aggregated postblit is generated which calls __fieldPostblit first and then __postblit.) $(LI void __xpostblit(). The field and aggregated postblits, although generated for a struct, are not actual struct members. In order to be able to call them, the compiler internally creates an alias, called __xpostblit which is a member of the struct and which points to the generated postblit that is the most inclusive.) ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_COMMENT // struct with alias __xpostblit = __postblit )$(D_KEYWORD struct) X { $(D_KEYWORD this)($(D_KEYWORD this)) {} } $(D_COMMENT // struct with alias __xpostblit = __fieldPostblit )$(D_COMMENT // which contains a call to X.__xpostblit )$(D_KEYWORD struct) Y { X a; } $(D_COMMENT // struct with alias __xpostblit = __aggrPostblit which contains )$(D_COMMENT // a call to Y.__xpostblit and a call to Z.__postblit )$(D_KEYWORD struct) Z { Y a; $(D_KEYWORD this)($(D_KEYWORD this)) {} } $(D_KEYWORD void) main() { $(D_COMMENT // X has __postblit and __xpostblit $(LPAREN)pointing to __postblit$(RPAREN ) ) $(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD __traits)(hasMember, X, $(D_STRING "__postblit"))); $(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD __traits)(hasMember, X, $(D_STRING "__xpostblit"))); $(D_COMMENT // Y does not have __postblit, but has __xpostblit $(LPAREN)pointing to __fieldPostblit$(RPAREN ) ) $(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(hasMember, Y, $(D_STRING "__postblit"))); $(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD __traits)(hasMember, Y, $(D_STRING "__xpostblit"))); $(D_COMMENT // __fieldPostblit is not a member of the struct ) $(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(hasMember, Y, $(D_STRING "__fieldPostblit"))); $(D_COMMENT // Z has __postblit and __xpostblit $(LPAREN)pointing to __aggrPostblit$(RPAREN ) ) $(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD __traits)(hasMember, Z, $(D_STRING "__postblit"))); $(D_KEYWORD static) $(D_KEYWORD assert)($(D_KEYWORD __traits)(hasMember, Z, $(D_STRING "__xpostblit"))); $(D_COMMENT // __aggrPostblit is not a member of the struct ) $(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(hasMember, Z, $(D_STRING "__aggrPostblit"))); } ) ) $(DDOC_BLANKLINE ) $(P Neither of the above postblits is defined for structs that don't define this(this) and don't have fields that transitively define it. If a struct does not define a postblit (implicit or explicit) but defines functions that use the same name/signature as the internally generated postblits, the compiler is able to identify that the functions are not actual postblits and does not insert calls to them when the struct is copied. Example:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) X {} $(D_KEYWORD int) a; $(D_KEYWORD struct) Y { $(D_KEYWORD int) a; X b; $(D_KEYWORD void) __fieldPostPostblit() { a = 42; } } $(D_KEYWORD void) main() { $(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(hasMember, X, $(D_STRING "__postblit"))); $(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(hasMember, X, $(D_STRING "__xpostblit"))); $(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(hasMember, Y, $(D_STRING "__postblit"))); $(D_KEYWORD static) $(D_KEYWORD assert)(!$(D_KEYWORD __traits)(hasMember, Y, $(D_STRING "__xpostblit"))); Y y; $(D_KEYWORD auto) y2 = y; $(D_KEYWORD assert)(a == 0); $(D_COMMENT // __fieldPostBlit does not get called )} ) ) $(DDOC_BLANKLINE ) $(P Postblits cannot be overloaded. If two or more postblits are defined, even if the signatures differ, the compiler assigns the __postblit name to both and later issues a conflicting function name error:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) X { $(D_KEYWORD this)($(D_KEYWORD this)) {} $(D_KEYWORD this)($(D_KEYWORD this)) $(D_KEYWORD const) {} $(D_COMMENT // error: function X.__postblit conflicts with function X.__postblit )} ) ) $(DDOC_BLANKLINE ) $(P The following describes the behavior of the qualified postblit definitions:) $(DDOC_BLANKLINE ) $(OL $(LI const. When a postblit is qualified with const as in $(D this(this) const;) or $(D const this(this);) then the postblit is successfully called on mutable (unqualified), const, and immutable objects, but the postblit cannot modify the object because it regards it as const; hence const postblits are of limited usefulness. Example:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) n; $(D_KEYWORD this)($(D_KEYWORD this)) $(D_KEYWORD const) { $(D_KEYWORD import) std.stdio : writeln; writeln($(D_STRING "postblit called")); $(D_COMMENT //++n; // error: cannot modify this.n in `const` function ) } } $(D_KEYWORD void) main() { S s1; $(D_KEYWORD auto) s2 = s1; $(D_KEYWORD const) S s3; $(D_KEYWORD auto) s4 = s3; $(D_KEYWORD immutable) S s5; $(D_KEYWORD auto) s6 = s5; } ) ) $(LI immutable. When a postblit is qualified with immutable as in $(D this(this) immutable) or $(D immutable this(this)) the code is ill-formed. The immutable postblit passes the compilation phase but cannot be invoked. Example:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) Y { $(D_COMMENT // not invoked anywhere, no error is issued ) $(D_KEYWORD this)($(D_KEYWORD this)) $(D_KEYWORD immutable) { } } $(D_KEYWORD struct) S { $(D_KEYWORD this)($(D_KEYWORD this)) $(D_KEYWORD immutable) { } } $(D_KEYWORD void) main() { S s1; $(D_KEYWORD auto) s2 = s1; $(D_COMMENT // error: immutable method `__postblit` is not callable using a mutable object ) $(D_KEYWORD const) S s3; $(D_KEYWORD auto) s4 = s3; $(D_COMMENT // error: immutable method `__postblit` is not callable using a mutable object ) $(D_KEYWORD immutable) S s5; $(D_KEYWORD auto) s6 = s5; $(D_COMMENT // error: immutable method `__postblit` is not callable using a mutable object )} ) ) $(DDOC_BLANKLINE ) $(LI shared. When a postblit is qualified with shared as in $(D this(this) shared) or $(D shared this(this)) solely shared objects may invoke the postblit; attempts of postbliting unshared objects will result in compile time errors:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD this)($(D_KEYWORD this)) $(D_KEYWORD shared) { } } $(D_KEYWORD void) main() { S s1; $(D_KEYWORD auto) s2 = s1; $(D_COMMENT // error: shared method `__postblit` is not callable using a non-shared object ) $(D_KEYWORD const) S s3; $(D_KEYWORD auto) s4 = s3; $(D_COMMENT // error: shared method `__postblit` is not callable using a non-shared object ) $(D_KEYWORD immutable) S s5; $(D_KEYWORD auto) s6 = s5; $(D_COMMENT // error: shared method `__postblit` is not callable using a non-shared object ) $(D_COMMENT // calling the shared postblit on a shared object is accepted ) $(D_KEYWORD shared) S s7; $(D_KEYWORD auto) s8 = s7; } ) ) ) $(DDOC_BLANKLINE ) $(P An unqualified postblit will get called even if the struct is instantiated as immutable or const, but the compiler issues an error if the struct is instantiated as shared:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) n; $(D_KEYWORD this)($(D_KEYWORD this)) { ++n; } } $(D_KEYWORD void) main() { $(D_KEYWORD immutable) S a; $(D_COMMENT // shared S a; => error : non-shared method is not callable using a shared object ) $(D_KEYWORD auto) a2 = a; $(D_KEYWORD import) std.stdio: writeln; writeln(a2.n); $(D_COMMENT // prints 1 )} ) ) $(DDOC_BLANKLINE ) $(P From a postblit perspective, qualifiying the struct definition yields the same result as explicitly qualifying the postblit.) $(DDOC_BLANKLINE ) $(P The following table lists all the possibilities of grouping qualifiers for a postblit associated with the type of object that needs to be used in order to successfully invoke the postblit:) $(DDOC_BLANKLINE ) $(TABLE_10 $(ARGS Qualifier Groups), $(VERTROW object type to be invoked on, $(D const), $(D immutable), $(D shared)), $(TROW any object type, $(YES ), $(NO ), $(NO ) ) $(TROW uncallable, $(NO ), $(YES ), $(NO ) ) $(TROW shared object, $(NO ), $(NO ), $(YES )) $(TROW uncallable, $(YES ), $(YES ), $(NO ) ) $(TROW shared object, $(YES ), $(NO ), $(YES )) $(TROW uncallable, $(NO ), $(YES ), $(YES )) $(TROW uncallable, $(YES ), $(YES ), $(YES )) ) $(DDOC_BLANKLINE ) $(P Note that when const and immutable are used to explicitly qualify a postblit as in this(this) const immutable; or const immutable this(this); - the order in which the qualifiers are declared does not matter - the compiler generates a conflicting attribute error, however declaring the struct as const/immutable and the postblit as immutable/const achieves the effect of applying both qualifiers to the postblit. In both cases the postblit is qualified with the more restrictive qualifier, which is immutable. ) $(DDOC_BLANKLINE ) $(P The postblits __fieldPostblit and __aggrPostblit are generated without any implicit qualifiers and are not considered struct members. This leads to the situation where qualifying an entire struct declaration with const or immutable does not have any impact on the above-mentioned postblits. However, since __xpostblit is a member of the struct and an alias of one of the other postblits, the qualifiers applied to the struct will affect the aliased postblit.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD this)($(D_KEYWORD this)) { } } $(D_COMMENT // `__xpostblit` aliases the aggregated postblit so the `const` applies to it. )$(D_COMMENT // However, the aggregated postblit calls the field postblit which does not have )$(D_COMMENT // any qualifier applied, resulting in a qualifier mismatch error )$(D_KEYWORD const) $(D_KEYWORD struct) B { S a; $(D_COMMENT // error : mutable method B.__fieldPostblit is not callable using a const object ) $(D_KEYWORD this)($(D_KEYWORD this)) { } } $(D_COMMENT // `__xpostblit` aliases the field postblit; no error )$(D_KEYWORD const) $(D_KEYWORD struct) B2 { S a; } $(D_COMMENT // Similar to B )$(D_KEYWORD immutable) $(D_KEYWORD struct) C { S a; $(D_COMMENT // error : mutable method C.__fieldPostblit is not callable using a immutable object ) $(D_KEYWORD this)($(D_KEYWORD this)) { } } $(D_COMMENT // Similar to B2, compiles )$(D_KEYWORD immutable) $(D_KEYWORD struct) C2 { S a; } ) ) $(DDOC_BLANKLINE ) $(P In the above situations the errors do not contain line numbers because the errors are regarding generated code. ) $(DDOC_BLANKLINE ) $(P Qualifying an entire struct as shared correctly propagates the attribute to the generated postblits:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD shared) $(D_KEYWORD struct) A { $(D_KEYWORD this)($(D_KEYWORD this)) { $(D_KEYWORD import) std.stdio : writeln; writeln($(D_STRING "the shared postblit was called")); } } $(D_KEYWORD struct) B { A a; } $(D_KEYWORD void) main() { $(D_KEYWORD shared) B b1; $(D_KEYWORD auto) b2 = b1; } ) ) $(DDOC_BLANKLINE ) $(P Unions may have fields that have postblits. However, a union itself never has a postblit. Copying a union does not result in postblit calls for any fields. If those calls are desired, they must be inserted explicitly by the programmer:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) count; $(D_KEYWORD this)($(D_KEYWORD this)) { ++count; } } $(D_KEYWORD union) U { S s; } $(D_KEYWORD void) main() { U a = U.init; U b = a; $(D_KEYWORD assert)(b.s.count == 0); b.s.__postblit; $(D_KEYWORD assert)(b.s.count == 1); } ) ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 StructDestructor, struct-destructor, Struct Destructors)

$(DDOC_BLANKLINE ) $(P Destructors are called implicitly when an object goes out of scope, or $(RELATIVE_LINK2 assign-overload, before an assignment) (by default). Their purpose is to free up resources owned by the struct object. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) i; ~$(D_KEYWORD this)() { $(D_KEYWORD import) std.stdio; writeln($(D_STRING "S$(LPAREN)"), i, $(D_STRING "$(RPAREN ) is being destructed")); } } $(D_KEYWORD void) main() { $(D_KEYWORD auto) s1 = S(1); { $(D_KEYWORD auto) s2 = S(2); $(D_COMMENT // s2 destructor called ) } S(3); $(D_COMMENT // s3 destructor called ) $(D_COMMENT // s1 destructor called )} ) ) $(P If the struct has a field of another struct type which itself has a destructor, that destructor will be called at the end of the parent destructor. If there is no parent destructor, the compiler will generate one. Similarly, a static array of a struct type with a destructor will have the destructor called for each element when the array goes out of scope.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD char) c; ~$(D_KEYWORD this)() { $(D_KEYWORD import) std.stdio; writeln($(D_STRING "S$(LPAREN)"), c, $(D_STRING "$(RPAREN ) is being destructed")); } } $(D_KEYWORD struct) Q { S a; S b; } $(D_KEYWORD void) main() { Q q = Q(S('a'), S('b')); S[2] arr = [S('0'), S('1')]; $(D_COMMENT // destructor called for arr[1], arr[0], q.b, q.a )} ) ) $(DDOC_BLANKLINE ) $(P A destructor for a struct instance can also be called early using $(REF1 destroy, object). Note that the destructor will still be called again when the instance goes out of scope.) $(DDOC_BLANKLINE ) $(P Struct destructors are used for $(DDSUBLINK spec/glossary, raii, RAII).) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 union-field-destruction, Union Field Destruction)

$(DDOC_BLANKLINE ) $(P Unions may have fields that have destructors. However, a union itself never has a destructor. When a union goes out of scope, destructors for its fields are not called. If those calls are desired, they must be inserted explicitly by the programmer:) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { ~$(D_KEYWORD this)() { $(D_KEYWORD import) std.stdio; writeln($(D_STRING "S is being destructed")); } } $(D_KEYWORD union) U { S s; } $(D_KEYWORD void) main() { $(D_KEYWORD import) std.stdio; { writeln($(D_STRING "entering first scope")); U u = U.init; $(D_KEYWORD scope) (exit) writeln($(D_STRING "exiting first scope")); } { writeln($(D_STRING "entering second scope")); U u = U.init; $(D_KEYWORD scope) (exit) { writeln($(D_STRING "exiting second scope")); destroy(u.s); } } } ) ) $(DDOC_BLANKLINE )

$(LNAME2 Invariant, Struct Invariants)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME Invariant): $(D invariant $(LPAREN) $(RPAREN )) $(GLINK2 statement, BlockStatement) $(D invariant) $(GLINK2 statement, BlockStatement) $(D invariant $(LPAREN)) $(GLINK2 expression, AssertArguments) $(D $(RPAREN ) ;) ) $(DDOC_BLANKLINE ) $(P Struct $(I Invariant)s specify the relationships among the members of a struct instance. Those relationships must hold for any interactions with the instance from its public interface. ) $(DDOC_BLANKLINE ) $(P The invariant is in the form of a $(D const) member function. The invariant is defined to $(I hold) if all the $(GLINK2 expression, AssertExpression)s within the invariant that are executed succeed. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) Date { $(D_KEYWORD this)($(D_KEYWORD int) d, $(D_KEYWORD int) h) { day = d; $(D_COMMENT // days are 1..31 ) hour = h; $(D_COMMENT // hours are 0..23 ) } $(D_KEYWORD invariant) { $(D_KEYWORD assert)(1 <= day && day <= 31); $(D_KEYWORD assert)(0 <= hour && hour < 24); } $(D_KEYWORD private): $(D_KEYWORD int) day; $(D_KEYWORD int) hour; } ) ) $(DDOC_BLANKLINE ) $(P There may be multiple invariants in a struct. They are applied in lexical order.) $(DDOC_BLANKLINE ) $(P Struct $(I Invariant)s must hold at the exit of the struct constructor (if any), and at the entry of the struct destructor (if any).) $(DDOC_BLANKLINE ) $(P Struct $(I Invariant)s must hold at the entry and exit of all public or exported non-static member functions. The order of application of invariants is: ) $(OL $(LI preconditions) $(LI invariant) $(LI function body) $(LI invariant) $(LI postconditions) ) $(DDOC_BLANKLINE ) $(P The invariant need not hold if the struct instance is implicitly constructed using the default $(D .init) value.) $(DDOC_BLANKLINE ) $(P If the invariant does not hold, then the program enters an invalid state.) $(DDOC_BLANKLINE ) $(IMPLEMENTATION_DEFINED $(OL $(LI Whether the struct $(I Invariant) is executed at runtime or not. This is typically controlled with a compiler switch.) $(LI The behavior when the invariant does not hold is typically the same as for when $(GLINK2 expression, AssertExpression)s fail.) ) ) $(DDOC_BLANKLINE ) $(UNDEFINED_BEHAVIOR happens if the invariant does not hold and execution continues.) $(DDOC_BLANKLINE ) $(P Public or exported non-static member functions cannot be called from within an invariant.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD struct) Foo { $(D_KEYWORD public) $(D_KEYWORD void) f() { } $(D_KEYWORD private) $(D_KEYWORD void) g() { } $(D_KEYWORD invariant) { f(); $(D_COMMENT // error, cannot call public member function from invariant ) g(); $(D_COMMENT // ok, g$(LPAREN)$(RPAREN ) is not public ) } } ) ) $(DDOC_BLANKLINE ) $(BEST_PRACTICE $(OL $(LI Do not indirectly call exported or public member functions within a struct invariant, as this can result in infinite recursion.) $(LI Avoid reliance on side effects in the invariant. as the invariant may or may not be executed.) $(LI Avoid having mutable public fields of structs with invariants, as then the invariant cannot verify the public interface.) ) ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 AssignOverload, assign-overload, Identity Assignment Overload)

$(DDOC_BLANKLINE ) $(P While copy construction takes care of initializing an object from another object of the same type, assignment is defined as copying the contents of a source object over those of a destination object, calling the destination object's destructor if it has one in the process: ) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD struct) S { ... } $(D_COMMENT // S has postblit or destructor )S s; $(D_COMMENT // default construction of s )S t = s; $(D_COMMENT // t is copy-constructed from s )t = s; $(D_COMMENT // t is assigned from s )) $(DDOC_BLANKLINE ) $(P Struct assignment $(CODE t=s) is defined to be semantically equivalent to: ) $(DDOC_BLANKLINE ) $(D_CODE t.opAssign(s); ) $(DDOC_BLANKLINE ) $(P where $(CODE opAssign) is a member function of S:) $(DDOC_BLANKLINE ) $(D_CODE $(D_KEYWORD ref) S opAssign($(D_KEYWORD ref) S s) { S tmp = $(D_KEYWORD this); $(D_COMMENT // bitcopy this into tmp ) $(D_KEYWORD this) = s; $(D_COMMENT // bitcopy s into this ) tmp.__dtor(); $(D_COMMENT // call destructor on tmp ) $(D_KEYWORD return) $(D_KEYWORD this); } ) $(DDOC_BLANKLINE ) $(P An identity assignment overload is required for a struct if one or more of these conditions hold:) $(DDOC_BLANKLINE ) $(UL $(LI it has a $(RELATIVE_LINK2 struct-destructor, destructor)) $(LI it has a $(RELATIVE_LINK2 struct-postblit, postblit)) $(LI it has a field with an identity assignment overload) ) $(DDOC_BLANKLINE ) $(P If an identity assignment overload is required and does not exist, an identity assignment overload function of the type $(CODE ref S opAssign(ref S)) will be automatically generated.) $(DDOC_BLANKLINE ) $(P A user-defined one can implement the equivalent semantics, but can be more efficient. ) $(DDOC_BLANKLINE ) $(P One reason a custom $(CODE opAssign) might be more efficient is if the struct has a reference to a local buffer: ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_COMPILE $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int)[] buf; $(D_KEYWORD int) a; $(D_KEYWORD ref) S opAssign($(D_KEYWORD ref) $(D_KEYWORD const) S s) $(D_KEYWORD return) { a = s.a; $(D_KEYWORD return) $(D_KEYWORD this); } $(D_KEYWORD this)($(D_KEYWORD this)) { buf = buf.dup; } } ) ) $(DDOC_BLANKLINE ) $(P Here, $(CODE S) has a temporary workspace $(CODE buf[]). The normal postblit will pointlessly free and reallocate it. The custom $(CODE opAssign) will reuse the existing storage. ) $(DDOC_BLANKLINE )

$(LEGACY_LNAME2 AliasThis, alias-this, Alias This)

$(DDOC_BLANKLINE ) $(GRAMMAR $(GNAME AliasThis): $(D alias) $(GLINK_LEX Identifier) $(D this ;) $(D alias) $(D this) $(D =) $(GLINK_LEX Identifier) $(D ;) ) $(DDOC_BLANKLINE ) $(P An $(I AliasThis) declaration names a member to subtype. The $(I Identifier) names that member. ) $(DDOC_BLANKLINE ) $(P A struct or union instance can be implicitly converted to the $(I AliasThis) member. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) x; $(D_KEYWORD alias) x $(D_KEYWORD this); } $(D_KEYWORD int) foo($(D_KEYWORD int) i) { $(D_KEYWORD return) i * 2; } $(D_KEYWORD void) main() { S s; s.x = 7; $(D_KEYWORD int) i = -s; $(D_KEYWORD assert)(i == -7); i = s + 8; $(D_KEYWORD assert)(i == 15); i = s + s; $(D_KEYWORD assert)(i == 14); i = 9 + s; $(D_KEYWORD assert)(i == 16); i = foo(s); $(D_COMMENT // implicit conversion to int ) $(D_KEYWORD assert)(i == 14); } ) ) $(DDOC_BLANKLINE ) $(P If the member is a class or struct, undefined lookups will be forwarded to the $(I AliasThis) member. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD class) Foo { $(D_KEYWORD int) baz = 4; $(D_KEYWORD int) get() { $(D_KEYWORD return) 7; } } $(D_KEYWORD struct) Bar { Foo foo; $(D_KEYWORD alias) foo $(D_KEYWORD this); } $(D_KEYWORD void) main() { Bar bar = Bar($(D_KEYWORD new) Foo()); $(D_KEYWORD int) i = bar.baz; $(D_KEYWORD assert)(i == 4); i = bar.get(); $(D_KEYWORD assert)(i == 7); } ) ) $(DDOC_BLANKLINE ) $(P If the $(I Identifier) refers to a property member function with no parameters then conversions and undefined lookups are forwarded to the return value of the function. ) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) x; @property $(D_KEYWORD int) get() { $(D_KEYWORD return) x * 2; } $(D_KEYWORD alias) get $(D_KEYWORD this); } $(D_KEYWORD void) main() { S s; s.x = 2; $(D_KEYWORD int) i = s; $(D_KEYWORD assert)(i == 4); } ) ) $(DDOC_BLANKLINE ) $(P If a struct declaration defines an $(D opCmp) or $(D opEquals) method, it will take precedence to that of the AliasThis member. Note that, unlike an $(D opCmp) method, an $(D opEquals) method is implicitly defined for a $(D struct) declaration if a user-defined one isn't provided. This means that if the AliasThis member's $(D opEquals) should be used, it must be explicitly defined: ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) S { $(D_KEYWORD int) a; $(D_KEYWORD bool) opEquals(S rhs) $(D_KEYWORD const) { $(D_KEYWORD return) $(D_KEYWORD this).a == rhs.a; } } $(D_KEYWORD struct) T { $(D_KEYWORD int) b; S s; $(D_KEYWORD alias) s $(D_KEYWORD this); } $(D_KEYWORD void) main() { S s1, s2; T t1, t2; $(D_KEYWORD assert)(s1 == s2); $(D_COMMENT // calls S.opEquals ) $(D_KEYWORD assert)(t1 == t2); $(D_COMMENT // calls compiler generated T.opEquals that implements member-wise equality ) $(D_KEYWORD assert)(s1 == t1); $(D_COMMENT // calls s1.opEquals$(LPAREN)t1.s$(RPAREN ); ) $(D_KEYWORD assert)(t1 == s1); $(D_COMMENT // calls t1.s.opEquals$(LPAREN)s1$(RPAREN ); )} ) ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD struct) U { $(D_KEYWORD int) a; $(D_KEYWORD bool) opCmp(U rhs) $(D_KEYWORD const) { $(D_KEYWORD return) $(D_KEYWORD this).a < rhs.a; } } $(D_KEYWORD struct) V { $(D_KEYWORD int) b; U u; $(D_KEYWORD alias) u $(D_KEYWORD this); } $(D_KEYWORD void) main() { U u1, u2; V v1, v2; $(D_KEYWORD assert)(!(u1 < u2)); $(D_COMMENT // calls U.opCmp ) $(D_KEYWORD assert)(!(v1 < v2)); $(D_COMMENT // calls U.opCmp because V does not define an opCmp method ) $(D_COMMENT // so the alias this of v1 is employed; U.opCmp expects a ) $(D_COMMENT // paramter of type U, so alias this of v2 is used ) $(D_KEYWORD assert)(!(u1 < v1)); $(D_COMMENT // calls u1.opCmp$(LPAREN)v1.u$(RPAREN ); ) $(D_KEYWORD assert)(!(v1 < u1)); $(D_COMMENT // calls v1.u.opCmp$(LPAREN)v1$(RPAREN ); )} ) ) $(P $(GLINK2 attribute, Attribute)s are ignored for $(D AliasThis). ) $(DDOC_BLANKLINE ) $(P A struct/union may only have a single $(I AliasThis) member.) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE ) $(DDOC_BLANKLINE )

$(LNAME2 nested, Nested Structs)

$(DDOC_BLANKLINE ) $(P A struct is a $(I nested struct) if) $(DDOC_BLANKLINE ) $(OL $(LI it is declared inside the scope of a function, or) $(LI it is a templated struct with one or more template arguments that alias local functions.) ) $(DDOC_BLANKLINE ) $(P A nested struct can have member functions. It has access to the context of its enclosing scope via a hidden field.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_RUN $(D_CODE $(D_KEYWORD void) foo() { $(D_KEYWORD int) i = 7; $(D_KEYWORD struct) SS { $(D_KEYWORD int) x,y; $(D_KEYWORD int) bar() { $(D_KEYWORD return) x + i + 1; } } SS s; s.x = 3; s.bar(); $(D_COMMENT // returns 11 )} ) ) $(DDOC_BLANKLINE ) $(P The static attribute will prevent a struct from being nested. As such, the struct will not have access to its enclosing scope.) $(DDOC_BLANKLINE ) $(SPEC_RUNNABLE_EXAMPLE_FAIL $(D_CODE $(D_KEYWORD void) foo() { $(D_KEYWORD int) i = 7; $(D_KEYWORD static) $(D_KEYWORD struct) SS { $(D_KEYWORD int) x, y; $(D_KEYWORD int) bar() { $(D_KEYWORD return) i; $(D_COMMENT // error, SS is not a nested struct ) } } } ) ) $(DDOC_BLANKLINE )

$(LNAME2 unions_and_special_memb_funct, Unions and Special Member Functions)

$(DDOC_BLANKLINE ) $(P Unions may not have postblits, destructors, or invariants.) $(DDOC_BLANKLINE ) $(SPEC_SUBNAV_PREV_NEXT hash-map, Associative Arrays, class, Classes) ) )