$(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 )
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 )
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 )
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 )
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 )
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 )
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 )
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 )
null
, is not acceptable.)
$(DDOC_BLANKLINE )
$(DDOC_BLANKLINE )
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 )
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 )
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 )
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 )
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 )
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 )