Movatterモバイル変換


[0]ホーム

URL:


D Logo
Menu
Search

Language Reference

table of contents

Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page.Requires a signed-in GitHub account. This works well for small changes.If you'd like to make larger changes you may want to consider usinga local clone.

Structs, Unions

Contents
  1. Introduction
  2. Members
    1. Struct Members
    2. Union Members
    3. Recursive Structs and Unions
  3. Struct Layout
  4. Plain Old Data
  5. Opaque Structs and Unions
  6. Initialization
    1. Default Initialization of Structs
    2. Static Initialization of Structs
    3. Default Initialization of Unions
    4. Static Initialization of Unions
    5. Dynamic Initialization of Structs
    6. Dynamic Initialization of Unions
  7. Struct Literals
  8. Union Literals
  9. Anonymous Structs and Unions
  10. Struct Properties
    1. Struct Field Properties
  11. Const, Immutable and Shared Structs
  12. Union Constructors
  13. Struct Constructors
    1. Delegating Constructors
    2. Struct Instantiation
    3. Constructor Attributes
    4. Disabling Default Struct Construction
    5. Field initialization inside a constructor
  14. Struct Copy Constructors
    1. Disabled Copying
    2. Copy Constructor Attributes
    3. Implicit Copy Constructors
  15. Struct Move Constructors
    1. Disabled Moving
    2. Move Constructor Attributes
    3. Implicit Move Constructors
  16. Struct Postblits
  17. Member Functions (a.k.a. Methods)
  18. Struct Destructors
  19. Union Field Destruction
  20. Struct Invariants
  21. Identity Assignment Overload
  22. Alias This
  23. Nested Structs
  24. Unions and Special Member Functions

Introduction

Whereasclasses are reference types, structs and unions are value types. Structs are simple aggregations of data and their associated operations on that data.

StructDeclaration:structIdentifier;structIdentifierAggregateBodyStructTemplateDeclarationAnonStructDeclaration
AnonStructDeclaration:structAggregateBody
UnionDeclaration:unionIdentifier;unionIdentifierAggregateBodyUnionTemplateDeclarationAnonUnionDeclaration
AnonUnionDeclaration:unionAggregateBody
AggregateBody:{DeclDefsopt}

The following example declares a struct type with a single integer field:

struct S{int i;}void main(){    S a;    a.i = 3;    S b = a;// copy a    a.i++;assert(a.i == 4);assert(b.i == 3);}

For local variables, a struct/union instance is allocated on the stack by default. To allocate on the heap, usenew, which gives a pointer.

Apointer to a struct or union is automatically dereferenced when using the. operator to access members.
S* p =new S;assert(p.i == 0);// `p.i` is the same as `(*p).i`
Note: There is no-> operator as in C.

A struct can contain multiple fields which are stored sequentially. Conversely, multiple fields in a union use overlapping storage.

union U{ubyte i;char c;}void main(){    U u;    u.i = 3;assert(u.c == '\x03');    u.c++;assert(u.i == 4);}

Members

Struct Members

A struct definition can contain:

A struct is defined to not have an identity; that is, the implementation is free to make bit copies of the struct as convenient.

Best Practices:
  1. Bit fields are supported with thebitfields template.

Union Members

A union definition can contain:

Recursive Structs and Unions

Structs and unions may not contain a non-static instance of themselves, however, they may contain a pointer to the same type.

struct S{    S* ptr;// OK    S[] slice;// OK    S s;// error    S[2] array;// errorstatic S global;// OK}

Struct Layout

The non-static data members of a struct are calledfields. Fields are laid out in lexical order. Fields are aligned according to theAlign 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.

extern(D) structs with no fields of non-zero size (akaEmpty Structs) have a size of one byte.

extern(C)struct C {}struct D {}staticassert(C.sizeof == 0);staticassert(D.sizeof == 1);

Non-staticfunction-nested D structs, which access the context of their enclosing scope, have an extra field.

Implementation Defined:
  1. The default layout of the fields of a struct is an exact match with theassociated C compiler.
  2. g++ and clang++ differ in how empty structs are handled. Both return1 fromsizeof, 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.
  3. clang and gcc both return0 fromsizeof for empty structs. Usingextern "C++" in clang++ and g++ does not cause them to conform to the behavior of their respective C compilers.
Undefined Behavior:
  1. The padding data can be accessed, but its contents are undefined.
  2. Do not pass or return structs with no fields of non-zero size toextern (C) functions. According to C11 6.7.2.1p8 this is undefined behavior.
Best Practices:
  1. When laying out a struct to match an externally defined layout, use align attributes to describe an exact match. Using aStatic Assert to ensure the result is as expected.
  2. Although the contents of the padding are often zero, do not rely on that.
  3. Avoid using empty structs when interfacing with C and C++ code.
  4. Avoid using empty structs as parameters or arguments to variadic functions.

Plain Old Data

A struct or union isPlain Old Data (POD) if it meets the following criteria:

  1. it is static, or not nested
  2. it has no postblits, copy constructors, destructors, or assignment operators
  3. it has no fields that are themselves non-POD
Best Practices: Structs or unions that interface with C code should be POD.

Opaque Structs and Unions

Opaque struct and union declarations do not have anAggregateBody:

struct S;union U;struct V(T);union W(T);

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:

struct S;S.sizeof;// error, size is not knownS s;// error, cannot initialize unknown contentsS* p;// ok, knowledge of members is not necessary
Best Practices: They can be used to implement thePIMPL idiom.

Initialization

Default Initialization of Structs

Struct fields are by default initialized to whatever theInitializer for the field is, and if none is supplied, to thedefault initializer for the field's type.

struct S {int a = 4;int b; }S x;// x.a is set to 4, x.b to 0

The default initializers are evaluated at compile time.

Static Initialization of Structs

StructInitializer:{StructMemberInitializersopt}
StructMemberInitializers:StructMemberInitializerStructMemberInitializer,StructMemberInitializer,StructMemberInitializers
StructMemberInitializer:NonVoidInitializerIdentifier:NonVoidInitializer

If aStructInitializer is supplied, eachStructMemberInitializer initializes a matching field:

Any field not covered by aStructMemberInitializer is default initialized.

struct S {int a, b, c, d = 7; }S r;// r.a = 0, r.b = 0, r.c = 0, r.d = 7S s = { a:1, b:2 };// s.a = 1, s.b = 2, s.c = 0, s.d = 7S t = { c:4, b:5, a:2, d:5 };// t.a = 2, t.b = 5, t.c = 4, t.d = 5S u = { 1, 2 };// u.a = 1, u.b = 2, u.c = 0, u.d = 7S v = { 1, d:3 };// v.a = 1, v.b = 0, v.c = 0, v.d = 3S w = { b:1, 3 };// w.a = 0, w.b = 1, w.c = 3, w.d = 7

Initializing a field more than once is an error:

S x = { 1, a:2 };// error: duplicate initializer for field `a`

Default Initialization of Unions

Unions are by default initialized to whatever theInitializer 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.

union U {int a = 4;long b; }U x;// x.a is set to 4, x.b to an implementation-defined value

It is an error to supply initializers for members other than the first one.

union V {int a;long b = 4; }// error: union field `b` with default initialization `4` must be before field `a`union W {int a = 4;long b = 5; }// error: overlapping default initialization for `a` and `b`

The default initializer is evaluated at compile time.

Implementation Defined: The values the fields other than the default initialized field are set to.

Static Initialization of Unions

Unions are initializedsimilarly to structs, except that only one member initializer is allowed. If the member initializer does not specify an identifier, it will initialize the first member of the union.

union U {int a;double b; }U u = { 2 };// u.a = 2U v = { b : 5.0 };// v.b = 5.0
U w = { 2, 3 };// error: overlapping initialization for field `a` and `b`

If the union is larger than the initialized field, the remaining bits are set to 0.

Implementation Defined: The values the fields other than the initialized field are set to.

Dynamic Initialization of Structs

Thestatic initializer syntax can also be used to initialize non-static variables. The initializer need not be evaluable at compile time.

struct S {int a, b, c, d = 7; }void test(int i){    S q = { 1, b:i };// q.a = 1, q.b = i, q.c = 0, q.d = 7}

Structs can be dynamically initialized from another value of the same type:

struct S {int a; }S t;// default initializedt.a = 3;S s = t;// s.a is set to 3

If the struct has aconstructor, and the struct is initialized with a value that is of a different type, then the constructor is called:

struct S{int a;this(int v)    {this.a = v;    }}S s = 3;// sets s.a to 3 using S's constructor

If the struct does not have a constructor butopCall is overridden for the struct, and the struct is initialized with a value that is of a different type, then theopCall operator is called:

struct S{int a;static S opCall(int v)    {        S s;        s.a = v;return s;    }static S opCall(S v)    {assert(0);    }}S s = 3;// sets s.a to 3 using S.opCall(int)S t = s;// sets t.a to 3, S.opCall(S) is not called

Dynamic Initialization of Unions

Thestatic initializer syntax can also be used to initialize non-static variables. The initializer need not be evaluable at compile time.

union U {int a;double b; }void test(int i){    U u = { a : i };// u.a = i    U v = { b : 5.0 };// v.b = 5.0}

Struct Literals

A struct literal consists of the name of the struct followed by a parenthesized named argument list:

struct S {int x;float y; }S s1 = S(1, 2);// set field x to 1, field y to 2S s2 = S(y: 2, x: 1);// same as aboveassert(s1 == s2);

If a struct has aconstructor or a member function namedopCall, then struct literals for that struct are not possible. See alsoopCall operator overloading for the issue workaround.

Struct literals are syntactically like function calls.

Arguments are assigned to fields as follows:
  1. If the first argument has no name, it will be assigned to the struct field that is defined first lexically.
  2. A named argument is assigned to the struct field with the same name. It is an error if no such field exists.
  3. Any other argument is assigned to the next lexically defined struct field relative to the preceding argument's struct field. It is an error if no such field exists, i.e. when the preceding argument assigns to the last struct field.
  4. It is also an error to assign a field more than once.
  5. Any fields not assigned a value are initialized with their respective default initializers.
Note: These rules are consistent with function calls, seeMatching Arguments to Parameters.

If there is a union field in the struct, only one member of the union can be initialized inside a struct literal. This matches the behaviour for union literals.

struct S {int x = 1, y = 2, z = 3; }S s0 = S(y: 5, 6, x: 4);// `6` is assigned to field `z`, which comes after `y`assert(s0.z == 6);S s1 = S(y: 5, z: 6);// Field x is not assigned, set to default initializer `1`assert(s1.x == 1);//S s2 = S(y: 5, x: 4, 5); // Error: field `y` is assigned twice//S s3 = S(z: 2, 3);       // Error: no field beyond `z`

Union Literals

A union literal is like a struct literal, but only one field can be initialized with an initializer expression. The remainder of the union's memory is initialized to zero.

union U{byte a;char[2] b;}U u = U(2);assert(u.a == 2);assert(u.b == [2, 0]);

Anonymous Structs and Unions

An anonymous struct or union can be declared as a member of a parent class, struct or union by omitting the identifier afterstruct orunion. An anonymous struct declares sequentially stored fields in the parent type. An anonymous union declares overlapping fields in the parent type.

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.

struct S{int a;union    {byte b;char c;    }}S s = S(1, 2);assert(s.a == 1);assert(s.b == 2);assert(s.c == 2);// overlaps with `b`

Conversely, an anonymous struct is useful inside a union to declare multiple fields that are stored sequentially.

union U{int a;struct    {uint b;bool c;    }}U u = U(1);assert(u.a == 1);assert(u.b == 1);// overlaps with `a`assert(u.c ==false);// no overlap

Struct Properties

NameDescription
.alignofSize boundary struct needs to be aligned on
.tupleofAsymbol sequence of all struct fields - seeclass.tupleof for more details.

Struct Field Properties

Struct Field Properties
NameDescription
.offsetofOffset in bytes of field from beginning of struct. Seethealign attribute for an example.

Const, Immutable and Shared Structs

A struct declaration can have a storage class ofconst,immutable orshared. It has an equivalent effect as declaring each member of the struct asconst,immutable orshared.

conststruct S {int a;int b = 2; }void main(){    S s = S(3);// initializes s.a to 3    S t;// initializes t.a to 0    t = s;// error, t.a and t.b are const, so cannot modify them.    t.a = 4;// error, t.a is const}

Union Constructors

Unions are constructed in the same way as structs.

Struct Constructors

Struct constructors are used to initialize an instance of a struct when a more complex construction is needed than is allowed bystatic initialization or astruct literal.

Constructors are defined with a function name ofthis and have no return value. The grammar is the same as for the classConstructor.

A struct constructor is called by the name of the struct followed byParameters.

If theParameterList is empty, the struct instance is default initialized.

struct S{int x, y = 4, z = 6;this(int a,int b)    {        x = a;        y = b;    }}void main(){    S a = S(4, 5);// calls S.this(4, 5):  a.x = 4, a.y = 5, a.z = 6    S b = S();// default initialized:    b.x = 0, b.y = 4, b.z = 6    S c = S(1);// error, matching this(int) not found}

Named arguments will be forwarded to the constructor and match parameter names, not struct field names.

struct S{int x;int y;this(int y,int z) {this.x = y;this.y = z; }}S a = S(x: 3, y: 4);// Error: constructor has no parameter named `x`S b = S(y: 3, 4);// `y: 3` will set field `x` through parameter `y`

Adefault constructor (i.e. one with an emptyParameterList) is not allowed.

struct S{int x;this() { }// error, struct default constructor not allowed}

Delegating Constructors

A constructor can call another constructor for the same struct in order to share common initializations. This is called adelegating constructor:

struct S{int j = 1;long k = 2;this(long k)    {this.k = k;    }this(int i)    {// At this point: j=1, k=2this(6);// delegating constructor call// At this point: j=1, k=6        j = i;// At this point: j=i, k=6    }}

The following restrictions apply:

  1. If a constructor's code contains a delegating constructor call, all possible execution paths through the constructor must make exactly one delegating constructor call:
    struct S{int a;this(int i) { }this(char c)    {        c ||this(1);// error, not on all paths    }this(wchar w)    {        (w) ?this(1) :this('c');// ok    }this(byte b)    {foreach (i; 0 .. b)        {this(1);// error, inside loop        }    }}
  2. It is illegal to refer tothis implicitly or explicitly prior to making a delegating constructor call.
  3. Once the delegating constructor returns, all fields are considered constructed.
  4. Delegating constructor calls cannot appear after labels.

See also:delegating class constructors.

Struct Instantiation

When an instance of a struct is created, the following steps happen:

  1. 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.
  2. If there is a constructor defined for the struct, the constructor matching the argument list is called.
  3. If struct invariant checking is turned on, the struct invariant is called at the end of the constructor.

Constructor Attributes

A constructor qualifier (const,immutable orshared) constructs the object instance with that specific qualifier.

struct S1{int[] a;this(int n) { a =newint[](n); }}struct S2{int[] a;this(int n)immutable { a =newint[](n); }}void main(){// Mutable constructor creates mutable object.    S1 m1 = S1(1);// Constructed mutable object is implicitly convertible to const.const S1 c1 = S1(1);// Constructed mutable object is not implicitly convertible to immutable.immutable i1 = S1(1);// error// Mutable constructor cannot construct immutable object.auto x1 =immutable S1(1);// error// Immutable constructor creates immutable object.immutable i2 =immutable S2(1);// Immutable constructor cannot construct mutable object.auto x2 = S2(1);// error// Constructed immutable object is not implicitly convertible to mutable.    S2 m2 =immutable S2(1);// error// Constructed immutable object is implicitly convertible to const.const S2 c2 =immutable S2(1);}

Constructors can be overloaded with different attributes.

struct S{this(int);// non-shared mutable constructorthis(int)shared;// shared mutable constructorthis(int)immutable;// immutable constructor}void fun(){    S m = S(1);shared s =shared S(2);immutable i =immutable S(3);}

Pure Constructors

If the constructor can create a unique object (i.e. if it ispure), the object is implicitly convertible to any qualifiers.

struct S{this(int)pure;// Based on the definition, this creates a mutable object. But the// created object cannot contain any mutable global data.// Therefore the created object is unique.this(int[] arr)immutablepure;// Based on the definition, this creates an immutable object. But// the argument int[] never appears in the created object so it// isn't implicitly convertible to immutable. Also, it cannot store// any immutable global data.// Therefore the created object is unique.}void fun(){immutable i =immutable S(1);// this(int) pure is calledshared s =shared S(1);// this(int) pure is called    S m = S([1,2,3]);// this(int[]) immutable pure is called}

Disabling Default Struct Construction

If a struct constructor is annotated with@disable and has an emptyParameterList, the struct has disabled default construction. The only way it can be constructed is via a call to another constructor with a non-emptyParameterList.

A struct with a disabled default constructor, and no other constructors, cannot be instantiated other than via aVoidInitializer.

A disabled default constructor may not have aFunctionBody.

If any fields have disabled default construction, struct default construction is also disabled.

struct S{int x;// Disables default construction    @disablethis();this(int v) { x = v; }}struct T{int y;    S s;}void main(){    S s;// error: default construction is disabled    S t = S();// error: also disabled    S u = S(1);// constructed by calling `S.this(1)`    S v =void;// not initialized, but allowed    S w = { 1 };// error: cannot use { } since constructor exists    S[3] a;// error: default construction is disabled    S[3] b = [S(1), S(20), S(-2)];// ok    T t;// error: default construction is disabled}
Best Practices: Disabling default construction is useful when the default value, such asnull, is not acceptable.

Field initialization inside a constructor

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 formfield = expression are treated as equivalent totypeof(field)(expression). The values of fields may be read before initialization or construction with a delegating constructor.

struct S{int num;int ber;this(int i)    {        num = i + 1;// initialization        num = i + 2;// assignment        ber = ber + 1;// ok to read before initialization    }this(int i,int j)    {this(i);        num = i + 1;// assignment    }}

If the field type has anopAssign method, it will not be used for initialization.

struct A{this(int n) {}void opAssign(A rhs) {}}struct S{    A val;this(int i)    {        val = A(i);// val is initialized to the value of A(i)        val = A(2);// rewritten to val.opAssign(A(2))    }}

If the field type is not mutable, multiple initialization will be rejected.

struct S{immutableint num;this(int)    {        num = 1;// OK        num = 2;// Error: assignment to immutable    }}

If the field is initialized on one path, it must be initialized on all paths.

struct S{immutableint num;immutableint ber;this(int i)    {if (i)            num = 3;// initializationelse            num = 4;// initialization    }this(long j)    {        j ? (num = 3) : (num = 4);// ok        j || (ber = 3);// Error: initialized on only one path        j && (ber = 3);// Error: initialized on only one path    }}

A field initialization may not appear in a loop or after a label.

struct S{immutableint num;immutable string str;this(int j)    {foreach (i; 0..j)        {            num = 1;// Error: field initialization not allowed in loops        }        size_t i = 0;    Label:        str ="hello";// Error: field initialization not allowed after labelsif (i++ < 2)goto Label;    }this(int j,int k)    {switch (j)        {case 1: ++j;break;default:break;        }        num = j;// Error: `case` and `default` are also labels    }}

If a field's type has disabled default construction, then it must be initialized in the constructor.

struct S {int y; @disablethis(); }struct T{    S s;this(S t) { s = t; }// okthis(int i) {this('c'); }// okthis(char) { }// Error: s not initialized}

Struct Copy Constructors

Warning: The plan is forcopy constructors to replacepostblit constructors, leaving postblit constructors only in legacy code. However, because the compiler hooks in druntime which deal with dynamic arrays and associative arrays have not yet all been updated to support copy constructors properly (issue #20970), any type that might be used in a dynamic array or associative array should use a postblit constructor rather than a copy constructor. The copy constructor will not be called in all the cases where it should be for the elements of a dynamic array or the keys or values in an associative array. Postblit constructors do not have this problem.

For backward compatibility reasons, astruct 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.

Copy constructors are used to initialize astruct instance from another instance of the same type. Astruct that defines a copy constructor is notPOD.

A constructor declaration is a copy constructor declaration if it meets the following requirements:

struct A{this(refreturnscope A rhs) {}// copy constructorthis(refreturnscopeconst A rhs,int b = 7) {}// copy constructor with default parameter}

The copy constructor is type checked as a normal constructor.

If a copy constructor is defined, implicit calls to it will be inserted in the following situations:

  1. When a variable is explicitly initialized:
  2. struct A{int[] arr;this(refreturnscope A rhs) { arr = rhs.arr.dup; }}void main(){    A a;    a.arr = [1, 2];    A b = a;// copy constructor gets called    b.arr[] += 1;assert(a.arr == [1, 2]);// a is unchangedassert(b.arr == [2, 3]);}
  3. When a parameter is passed by value to a function:
  4. struct A{this(refreturnscope A another) {}}void fun(A a) {}void main(){    A a;    fun(a);// copy constructor gets called}
  5. When a parameter is returned by value from a function and Named Returned Value Optimization (NRVO) cannot be performed:
  6. struct A{this(refreturnscope A another) {}}A fun(){    A a;return a;// NRVO, no copy constructor call}A a;A gun(){return a;// cannot perform NRVO, rewrite to: return (A __tmp; __tmp.copyCtor(a));}void main(){    A a = fun();    A b = gun();}

Disabled Copying

When a copy constructor is defined for astruct (or marked@disable), the compiler no longer implicitly generates default copy/blitting constructors for thatstruct:

struct A{int[] a;this(refreturnscope A rhs) {}}void fun(immutable A) {}void main(){immutable A a;    fun(a);// error: copy constructor cannot be called with types (immutable) immutable}
struct A{    @disablethis(ref A);}void main(){    A a;    A b = a;// error: copy constructor is disabled}

If aunion U has fields that define a copy constructor, whenever an object of typeU is initialized by copy, an error will be issued. The same rule applies to overlapped fields (anonymous unions).

struct S{this(ref S);}union U{    S s;}void main(){    U a;    U b = a;// error, could not generate copy constructor for U}

Copy Constructor Attributes

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):

struct A{this(refreturnscope A another) {}// 1 - mutable source, mutable destinationthis(refreturnscopeimmutable A another) {}// 2 - immutable source, mutable destinationthis(refreturnscope A another)immutable {}// 3 - mutable source, immutable destinationthis(refreturnscopeimmutable A another)immutable {}// 4 - immutable source, immutable destination}void main(){    A a;immutable A ia;    A a2 = a;// calls 1    A a3 = ia;// calls 2immutable A a4 = a;// calls 3immutable A a5 = ia;// calls 4}

Theinout qualifier may be applied to the copy constructor parameter in order to specify that mutable,const, orimmutable types are treated the same:

struct A{this(refreturnscopeinout A rhs)immutable {}}void main(){    A r1;const(A) r2;immutable(A) r3;// All call the same copy constructor because `inout` acts like a wildcardimmutable(A) a = r1;immutable(A) b = r2;immutable(A) c = r3;}

Implicit Copy Constructors

A copy constructor is generated implicitly by the compiler for astruct S if all of the following conditions are met:

  1. S does not explicitly declare any copy constructors;
  2. S defines at least one direct member that has a copy constructor, and that member is not overlapped (by means ofunion) with any other member.

If the restrictions above are met, the following copy constructor is generated:

this(refreturnscopeinout(S) src)inout{foreach (i,refinout field; src.tupleof)this.tupleof[i] = field;}

If the generated copy constructor fails to type check, it will receive the@disable attribute.

Struct Move Constructors

Move constructors are very much like copy constructors. The difference is that copy constructors make a copy of the original, while move constructors move the contents of the original, and the lifetime of the original ends.

Note: Do not use postblits in the same struct with move constructors.

If a move constructor is declared, also declare a copy constructor.

A constructor declaration is a move constructor declaration if it meets the following requirements:

struct A{this(refreturnscope A rhs) {}// copy constructorthis(returnscope A rhs) {}// move constructorthis(returnscopeconst A rhs,int b = 7) {}// move constructor with default parameter}

The move constructor is type checked as a normal constructor.

The move constructor's first parameter only accepts rvalues. An lvalue can be coerced into being an rvalue using__rvalue(Expression).

If a move constructor is defined, implicit calls to it will be inserted in the following situations:

  1. When a variable is explicitly initialized:
  2. struct A{int[] arr;this(returnscope A rhs)// move constructor    {        arr = rhs.arr;        rhs.arr =null;// do not leave dangling reference to array    }this(refreturnscope A rhs) {assert(0); }}void main(){    A a;    a.arr = [1, 2];    A b =__rvalue(a);// move constructor gets called    b.arr[] += 1;assert(a.arrisnull);// a.arr is goneassert(b.arr == [2, 3]);}
  3. When a parameter is passed by value to a function:
  4. struct A{this(returnscope A another) {}// move constructorthis(refreturnscope A rhs) {assert(0); }}void fun(A a) {}void main(){    A a;    fun(__rvalue(a));// move constructor gets called// `a` is no longer a valid object}

Disabled Moving

When a move constructor is defined for astruct (or marked@disable), the compiler no longerimplicitly generates default move constructors for thatstruct:

struct A{this(ref A);    @disablethis(A);}void main(){    A a;    A b =__rvalue(a);// error: move constructor is disabled}

If aunion U has fields that define a move constructor, whenever an object of typeU is initialized by move, an error will be issued. The same rule applies to overlapped fields (anonymous unions).

struct S{this(ref S);this(S);}union U{    S s;}void main(){    U a;    U b =__rvalue(a);// error, could not generate move constructor for U}

Move Constructor Attributes

The move constructor can be overloaded with different qualifiers applied to the parameter (moving from a qualified source) or to the move constructor itself (moving to a qualified destination):

struct A{this(refreturnscope A another) {assert(0); }// copy constructorthis(returnscope A another) {}// 1 - mutable source, mutable destinationthis(returnscopeimmutable A another) {}// 2 - immutable source, mutable destinationthis(returnscope A another)immutable {}// 3 - mutable source, immutable destinationthis(returnscopeimmutable A another)immutable {}// 4 - immutable source, immutable destination}void main(){    A a;immutable A ia;    A a2 =__rvalue(a);// calls 1    A a3 =__rvalue(ia);// calls 2    A b;immutable A ib;immutable A b4 =__rvalue(b);// calls 3immutable A b5 =__rvalue(ib);// calls 4}

Theinout qualifier may be applied to the move constructor parameter in order to specify that mutable,const, orimmutable types are treated the same:

struct A{this(refreturnscopeinout A rhs)immutable {assert(0); }this(returnscopeinout A rhs)immutable {}}void main(){    A r1;const(A) r2;immutable(A) r3;// All call the same move constructor because `inout` acts like a wildcardimmutable(A) a =__rvalue(r1);immutable(A) b =__rvalue(r2);immutable(A) c =__rvalue(r3);}

Implicit Move Constructors

A move constructor is generated implicitly by the compiler for astruct S if all of the following conditions are met:

  1. S does not explicitly declare any move constructors;
  2. S defines at least one direct member that has a move constructor, and that member is not overlapped (by means ofunion) with any other member.

If the restrictions above are met, the following move constructor is generated:

this(returnscopeinout(S) src)inout{foreach (i,refinout field; src.tupleof)this.tupleof[i] =__rvalue(field);}

If the generated move constructor fails to type check, it will receive the@disable attribute.

import core.stdc.stdio;struct T{int i;inoutthis(refinout T t) {this.i = t.i - 1; printf("this(ref T)\n"); }inoutthis(inout T t)     {this.i = t.i + 1; printf("this(T)\n"); }}struct S{    T t;}void main(){    S s;    s.t.i = 3;    S u = s;    printf("u.t.i = %d\n", u.t.i);assert(u.t.i == 2);    S v =__rvalue(u);    printf("v.t.i = %d\n", v.t.i);assert(v.t.i == 3);}

Struct Postblits

Postblit:this ( this )MemberFunctionAttributesoptFunctionBodythis ( this )MemberFunctionAttributesoptMissingFunctionBody

Warning: The plan is forcopy constructors to replacepostblit constructors, leaving postblit constructors only in legacy code. However, because the compiler hooks in druntime which deal with dynamic arrays and associative arrays have not yet all been updated to support copy constructors properly (issue #20970), any type that might be used in a dynamic array or associative array should use a postblit constructor rather than a copy constructor. The copy constructor will not be called in all the cases where it should be for the elements of a dynamic array or the keys or values in an associative array. Postblit constructors do not have this problem.

For backward compatibility reasons, astruct 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.

Copy construction is defined as initializing a struct instance from another instance of the same type. Copy construction is divided into two parts:

  1. blitting the fields, i.e. copying the bits
  2. runningpostblit on the result

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 ‘fix up’ the destination as necessary, such as making copies of referenced data, incrementing reference counts, etc. For example:

struct S{int[] a;// array is privately owned by this instancethis(this)    {        a = a.dup;    }}

Disabling struct postblit makes the object not copyable.

struct T{    @disablethis(this);// disabling makes T not copyable}struct S{    T t;// uncopyable member makes S also not copyable}void main(){    S s;    S t = s;// error, S is not copyable}

Depending on the struct layout, the compiler may generate the following internal postblit functions:

  1. void __postblit(). The compiler assigns this name to the explicitly defined postblitthis(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.
  2. void __fieldPostblit(). If a structX has at least onestruct member that in turn defines (explicitly or implicitly) a postblit, then a field postblit is generated forX that calls all the underlying postblits of the struct fields in declaration order.
  3. 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.
  4. 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.
// struct with alias __xpostblit = __postblitstruct X{this(this) {}}// struct with alias __xpostblit = __fieldPostblit// which contains a call to X.__xpostblitstruct Y{    X a;}// struct with alias __xpostblit = __aggrPostblit which contains// a call to Y.__xpostblit and a call to Z.__postblitstruct Z{    Y a;this(this) {}}void main(){// X has __postblit and __xpostblit (pointing to __postblit)staticassert(__traits(hasMember, X,"__postblit"));staticassert(__traits(hasMember, X,"__xpostblit"));// Y does not have __postblit, but has __xpostblit (pointing to __fieldPostblit)staticassert(!__traits(hasMember, Y,"__postblit"));staticassert(__traits(hasMember, Y,"__xpostblit"));// __fieldPostblit is not a member of the structstaticassert(!__traits(hasMember, Y,"__fieldPostblit"));// Z has  __postblit and __xpostblit (pointing to __aggrPostblit)staticassert(__traits(hasMember, Z,"__postblit"));staticassert(__traits(hasMember, Z,"__xpostblit"));// __aggrPostblit is not a member of the structstaticassert(!__traits(hasMember, Z,"__aggrPostblit"));}

Neither of the above postblits is defined for structs that don't definethis(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:

struct X{}int a;struct Y{int a;    X b;void __fieldPostPostblit()    {        a = 42;    }}void main(){staticassert(!__traits(hasMember, X,"__postblit"));staticassert(!__traits(hasMember, X,"__xpostblit"));staticassert(!__traits(hasMember, Y,"__postblit"));staticassert(!__traits(hasMember, Y,"__xpostblit"));    Y y;auto y2 = y;assert(a == 0);// __fieldPostBlit does not get called}

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:

struct X{this(this) {}this(this)const {}// error: function X.__postblit conflicts with function X.__postblit}

The following describes the behavior of the qualified postblit definitions:

  1. const. When a postblit is qualified withconst as inthis(this) const; orconst this(this); then the postblit is successfully called on mutable (unqualified),const, andimmutable objects, but the postblit cannot modify the object because it regards it asconst; henceconst postblits are of limited usefulness. Example:
  2. struct S{int n;this(this)const    {import std.stdio : writeln;        writeln("postblit called");//++n; // error: cannot modify this.n in `const` function    }}void main(){    S s1;auto s2 = s1;const S s3;auto s4 = s3;immutable S s5;auto s6 = s5;}
  3. immutable. When a postblit is qualified withimmutable as inthis(this) immutable orimmutable this(this) the code is ill-formed. Theimmutable postblit passes the compilation phase but cannot be invoked. Example:
  4. struct Y{// not invoked anywhere, no error is issuedthis(this)immutable    { }}struct S{this(this)immutable    { }}void main(){    S s1;auto s2 = s1;// error: immutable method `__postblit` is not callable using a mutable objectconst S s3;auto s4 = s3;// error: immutable method `__postblit` is not callable using a mutable objectimmutable S s5;auto s6 = s5;// error: immutable method `__postblit` is not callable using a mutable object}
  5. shared. When a postblit is qualified withshared as inthis(this) shared orshared this(this) solelyshared objects may invoke the postblit; attempts of postbliting unshared objects will result in compile time errors:
  6. struct S{this(this)shared    { }}void main(){    S s1;auto s2 = s1;// error: shared method `__postblit` is not callable using a non-shared objectconst S s3;auto s4 = s3;// error: shared method `__postblit` is not callable using a non-shared objectimmutable S s5;auto s6 = s5;// error: shared method `__postblit` is not callable using a non-shared object// calling the shared postblit on a shared object is acceptedshared S s7;auto s8 = s7;}

An unqualified postblit will get called even if the struct is instantiated asimmutable orconst, but the compiler issues an error if the struct is instantiated asshared:

struct S{int n;this(this) { ++n; }}void main(){immutable S a;// shared S a; => error : non-shared method is not callable using a shared objectauto a2 = a;import std.stdio: writeln;    writeln(a2.n);// prints 1}

From a postblit perspective, qualifiying the struct definition yields the same result as explicitly qualifying the postblit.

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:

Qualifier Groups
object type to be invoked onconstimmutableshared
any object type
uncallable
shared object
uncallable
shared object
uncallable
uncallable

Note that whenconst andimmutable are used to explicitly qualify a postblit as inthis(this) const immutable; orconst 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 asconst/immutable and the postblit asimmutable/const achieves the effect of applying both qualifiers to the postblit. In both cases the postblit is qualified with the more restrictive qualifier, which isimmutable.

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 withconst orimmutable 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.

struct S{this(this)    { }}// `__xpostblit` aliases the aggregated postblit so the `const` applies to it.// However, the aggregated postblit calls the field postblit which does not have// any qualifier applied, resulting in a qualifier mismatch errorconststruct B{    S a;// error : mutable method B.__fieldPostblit is not callable using a const objectthis(this)    { }}// `__xpostblit` aliases the field postblit; no errorconststruct B2{    S a;}// Similar to Bimmutablestruct C{    S a;// error : mutable method C.__fieldPostblit is not callable using a immutable objectthis(this)    { }}// Similar to B2, compilesimmutablestruct C2{    S a;}

In the above situations the errors do not contain line numbers because the errors are regarding generated code.

Qualifying an entire struct asshared correctly propagates the attribute to the generated postblits:

sharedstruct A{this(this)    {import std.stdio : writeln;        writeln("the shared postblit was called");    }}struct B{    A a;}void main(){shared B b1;auto b2 = b1;}

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:

struct S{int count;this(this)    {        ++count;    }}union U{    S s;}void main(){    U a = U.init;    U b = a;assert(b.s.count == 0);    b.s.__postblit;assert(b.s.count == 1);}

Member Functions (a.k.a. Methods)

A struct/union can have non-static member functions,like classes. Such functions (called instance methods) have a hiddenthis parameter which is a reference to the struct instance. However, an instance method can still be called on an rvalue struct instance, even if the method is not const:

struct S{int i;int f() => ++i;}void main(){//S().i++; // cannot modify, `S().i` is not an lvalueassert(S().f() == 1);// OK}
Rationale: An instance method may have other side effects besides mutating a field, or it may produce a useful return value. In the general case, throwing away changes to a field after the method returns does not necessarily indicate a logic error.

Struct Destructors

Destructors are called implicitly when an object goes out of scope, orbefore an assignment (by default). Their purpose is to free up resources owned by the struct object.

struct S{int i;    ~this()    {import std.stdio;        writeln("S(", i,") is being destructed");    }}void main(){auto s1 = S(1);    {auto s2 = S(2);// s2 destructor called    }    S(3);// s3 destructor called// s1 destructor called}

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.

struct S{char c;    ~this()    {import std.stdio;        writeln("S(", c,") is being destructed");    }}struct Q{    S a;    S b;}void main(){    Q q = Q(S('a'), S('b'));    S[2] arr = [S('0'), S('1')];// destructor called for arr[1], arr[0], q.b, q.a}

A destructor for a struct instance can also be called early usingdestroy. Note that the destructor will still be called again when the instance goes out of scope.

Struct destructors are used forRAII.

Union Field Destruction

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 fieldsare not called. If those calls are desired, they must be inserted explicitly by the programmer:

struct S{    ~this()    {import std.stdio;        writeln("S is being destructed");    }}union U{    S s;}void main(){import std.stdio;    {        writeln("entering first scope");        U u = U.init;scope (exit) writeln("exiting first scope");    }    {        writeln("entering second scope");        U u = U.init;scope (exit)        {            writeln("exiting second scope");            destroy(u.s);        }    }}

Struct Invariants

Invariant:invariant ( )BlockStatementinvariantBlockStatementinvariant (AssertArguments) ;

StructInvariants specify the relationships among the members of a struct instance. Those relationships must hold for any interactions with the instance from its public interface.

The invariant is in the form of aconst member function. The invariant is defined tohold if all theAssertExpressions within the invariant that are executed succeed.

struct Date{this(int d,int h)    {        day = d;// days are 1..31        hour = h;// hours are 0..23    }invariant    {assert(1 <= day && day <= 31);assert(0 <= hour && hour < 24);    }private:int day;int hour;}

There may be multiple invariants in a struct. They are applied in lexical order.

StructInvariants must hold at the exit of the struct constructor (if any), and at the entry of the struct destructor (if any).

StructInvariants must hold at the entry and exit of all public or exported non-static member functions. The order of application of invariants is:

  1. preconditions
  2. invariant
  3. function body
  4. invariant
  5. postconditions

The invariant need not hold if the struct instance is implicitly constructed using the default.init value.

If the invariant does not hold, then the program enters an invalid state.

Implementation Defined:
  1. Whether the structInvariant is executed at runtime or not. This is typically controlled with a compiler switch.
  2. The behavior when the invariant does not hold is typically the same as for whenAssertExpressions fail.
Undefined Behavior: happens if the invariant does not hold and execution continues.

Public or exported non-static member functions cannot be called from within an invariant.

struct Foo{publicvoid f() { }privatevoid g() { }invariant    {        f();// error, cannot call public member function from invariant        g();// ok, g() is not public    }}
Best Practices:
  1. Do not indirectly call exported or public member functions within a struct invariant, as this can result in infinite recursion.
  2. Avoid reliance on side effects in the invariant. as the invariant may or may not be executed.
  3. Avoid having mutable public fields of structs with invariants, as then the invariant cannot verify the public interface.

Identity Assignment Overload

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:

struct S { ... }// S has postblit or destructorS s;// default construction of sS t = s;// t is copy-constructed from st = s;// t is assigned from s

Struct assignmentt=s is defined to be semantically equivalent to:

t.opAssign(s);

whereopAssign is a member function of S:

ref S opAssign(ref S s){    S tmp =this;// bitcopy this into tmpthis = s;// bitcopy s into this    tmp.__dtor();// call destructor on tmpreturnthis;}

An identity assignment overload is required for a struct if one or more of these conditions hold:

If an identity assignment overload is required and does not exist, an identity assignment overload function of the typeref S opAssign(ref S) will be automatically generated.

A user-defined one can implement the equivalent semantics, but can be more efficient.

One reason a customopAssign might be more efficient is if the struct has a reference to a local buffer:

struct S{int[] buf;int a;ref S opAssign(refconst S s)return    {        a = s.a;returnthis;    }this(this)    {        buf = buf.dup;    }}

Here,S has a temporary workspacebuf[]. The normal postblit will pointlessly free and reallocate it. The customopAssign will reuse the existing storage.

Alias This

AliasThis:aliasIdentifierthis ;aliasthis=Identifier;

AnAliasThis declaration names a member to subtype. TheIdentifier names that member.

A struct or union instance can be implicitly converted to theAliasThis member.

struct S{int x;alias xthis;}int foo(int i) {return i * 2; }void main(){    S s;    s.x = 7;int i = -s;assert(i == -7);    i = s + 8;assert(i == 15);    i = s + s;assert(i == 14);    i = 9 + s;assert(i == 16);    i = foo(s);// implicit conversion to intassert(i == 14);}

If the member is a class or struct, undefined lookups will be forwarded to theAliasThis member.

class Foo{int baz = 4;int get() {return 7; }}struct Bar{    Foo foo;alias foothis;}void main(){    Bar bar = Bar(new Foo());int i = bar.baz;assert(i == 4);    i = bar.get();assert(i == 7);}

If theIdentifier refers to a property member function with no parameters then conversions and undefined lookups are forwarded to the return value of the function.

struct S{int x;    @propertyint get()    {return x * 2;    }alias getthis;}void main(){    S s;    s.x = 2;int i = s;assert(i == 4);}

If a struct declaration defines anopCmp oropEquals method, it will take precedence to that of theAliasThis member. Note that, unlike anopCmp method, anopEquals method is implicitly defined for astruct declaration if a user-defined one isn't provided. This means that if theAliasThis member'sopEquals should be used, it must be explicitly defined:

struct S{int a;bool opEquals(S rhs)const    {returnthis.a == rhs.a;    }}struct T{int b;    S s;alias sthis;}void main(){    S s1, s2;    T t1, t2;assert(s1 == s2);// calls S.opEqualsassert(t1 == t2);// calls compiler generated T.opEquals that implements member-wise equalityassert(s1 == t1);// calls s1.opEquals(t1.s);assert(t1 == s1);// calls t1.s.opEquals(s1);}
struct U{int a;bool opCmp(U rhs)const    {returnthis.a < rhs.a;    }}struct V{int b;    U u;alias uthis;}void main(){    U u1, u2;    V v1, v2;assert(!(u1 < u2));// calls U.opCmpassert(!(v1 < v2));// calls U.opCmp because V does not define an opCmp method// so the alias this of v1 is employed; U.opCmp expects a// paramter of type U, so alias this of v2 is usedassert(!(u1 < v1));// calls u1.opCmp(v1.u);assert(!(v1 < u1));// calls v1.u.opCmp(v1);}

Attributes are ignored forAliasThis.

A struct/union may only have a singleAliasThis member.

Nested Structs

A struct is anested struct if

  1. it is declared inside the scope of a function, or
  2. it is a templated struct with one or more template arguments that alias local functions.

A nested struct can have member functions. It has access to the context of its enclosing scope via a hidden field.

void foo(){int i = 7;struct SS    {int x,y;int bar() {return x + i + 1; }    }    SS s;    s.x = 3;    s.bar();// returns 11}

Thestatic attribute will prevent a struct from being nested. As such, the struct will not have access to its enclosing scope.

void foo(){int i = 7;staticstruct SS    {int x, y;int bar()        {return i;// error, SS is not a nested struct        }    }}

Warning: For nested structs,.init is not the same as default construction.

Unions and Special Member Functions

Unions may not have postblits, destructors, or invariants.

Associative Arrays
Classes
Copyright © 1999-2025 by theD Language Foundation | Page generated byDdoc on Fri Oct 10 22:16:57 2025

[8]ページ先頭

©2009-2025 Movatter.jp