Ddoc
$(SPEC_S Structs & Unions,
$(P Whereas classes are reference types, structs are value types.
Any C struct can be exactly represented as a D struct.
In C++ parlance, a D struct is a
$(LINK2 glossary.html#pod, POD (Plain Old Data)) type,
with a trivial constructors and destructors.
Structs and unions are meant as simple aggregations of data, or as a way
to paint a data structure over hardware or an external type. External
types can be defined by the operating system API, or by a file format.
Object oriented features are provided with the class data type.
)
$(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.)
$(TABLE2 Struct$(COMMA) Class Comparison Table,
$(TR
$(TH Feature)
$(TH struct)
$(TH class)
$(TH C struct)
$(TH C++ struct)
$(TH C++ class)
)
$(TR
$(TD value type)
$(YES)
$(NO)
$(YES)
$(YES)
$(YES)
)
$(TR
$(TD reference type)
$(NO)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD data members)
$(YES)
$(YES)
$(YES)
$(YES)
$(YES)
)
$(TR
$(TD hidden members)
$(V1 $(NO))
$(V2 $(YES))
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD static members)
$(YES)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD default member initializers)
$(YES)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD bit fields)
$(NO)
$(NO)
$(YES)
$(YES)
$(YES)
)
$(TR
$(TD non-virtual member functions)
$(YES)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD virtual member functions)
$(NO)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(V1
$(TR
$(TD constructors)
$(NO)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD destructors)
$(NO)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD RAII)
$(NO)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD assign overload)
$(NO)
$(NO)
$(NO)
$(YES)
$(YES)
)
)
$(V2
$(TR
$(TD $(LINK2 #Struct-Constructor, constructors))
$(YES)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD $(LINK2 #StructPostblit, postblit)/copy constructors)
$(YES)
$(NO)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD $(LINK2 #StructDestructor, destructors))
$(YES)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD $(LINK2 class.html#SharedStaticConstructor, shared static constructors))
$(YES)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD $(LINK2 class.html#SharedStaticDestructor, shared static destructors))
$(YES)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD RAII)
$(YES)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD $(LINK2 #AssignOverload, assign overload))
$(YES)
$(NO)
$(NO)
$(YES)
$(YES)
)
)
$(TR
$(TD $(LINK2 #StructLiteral, literals))
$(YES)
$(NO)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD operator overloading)
$(YES)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD inheritance)
$(NO)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD invariants)
$(YES)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD unit tests)
$(YES)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD synchronizable)
$(NO)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD parameterizable)
$(YES)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD alignment control)
$(YES)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD member protection)
$(YES)
$(YES)
$(NO)
$(YES)
$(YES)
)
$(TR
$(TD default public)
$(YES)
$(YES)
$(YES)
$(YES)
$(NO)
)
$(TR
$(TD tag name space)
$(NO)
$(NO)
$(YES)
$(YES)
$(YES)
)
$(TR
$(TD anonymous)
$(YES)
$(NO)
$(YES)
$(YES)
$(YES)
)
$(TR
$(TD static constructor)
$(YES)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD static destructor)
$(YES)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(V2
$(TR
$(TD const/immutable/shared)
$(YES)
$(YES)
$(NO)
$(NO)
$(NO)
)
$(TR
$(TD inner nesting)
$(TD $(LINK2 #nested, YES))
$(TD $(LINK2 class.html#nested, YES))
$(NO)
$(NO)
$(NO)
)
)
)
$(GRAMMAR
$(GNAME AggregateDeclaration):
$(B struct) $(I Identifier) $(GLINK StructBody)
$(B union) $(I Identifier) $(GLINK StructBody)
$(B struct) $(I Identifier) $(B ;)
$(B union) $(I Identifier) $(B ;)
$(LINK2 template.html#StructTemplateDeclaration, $(I StructTemplateDeclaration))
$(LINK2 template.html#UnionTemplateDeclaration, $(I UnionTemplateDeclaration))
$(GNAME StructBody):
$(B {) $(B })
$(B {) $(GLINK StructBodyDeclarations) $(B })
$(GNAME StructBodyDeclarations):
$(GLINK StructBodyDeclaration)
$(GLINK StructBodyDeclaration) $(I StructBodyDeclarations)
$(GNAME StructBodyDeclaration):
$(LINK2 module.html#DeclDef, $(I DeclDef))
$(GLINK StructAllocator)
$(GLINK StructDeallocator)
$(V2 $(GLINK StructPostblit)
$(LINK2 class.html#AliasThis, $(I AliasThis)))
$(GNAME StructAllocator):
$(LINK2 class.html#ClassAllocator, $(I ClassAllocator))
$(GNAME StructDeallocator):
$(LINK2 class.html#ClassDeallocator, $(I ClassDeallocator))
)
$(P They work like they do in C, with the following exceptions:)
$(UL
$(LI no bit fields)
$(LI alignment can be explicitly specified)
$(LI no separate tag name space - tag names go into the current scope)
$(LI declarations like:
------
struct ABC x;
------
are not allowed, replace with:
------
ABC x;
------
)
$(LI anonymous structs/unions are allowed as members of other structs/unions)
$(LI Default initializers for members can be supplied.)
$(LI Member functions and static members are allowed.)
)
Static Initialization of Structs
Static struct members are by default initialized to whatever the
default initializer for the member is, and if none supplied, to
the default initializer for the member's type.
If a static initializer is supplied, the
members are initialized by the member name,
colon, expression syntax. The members may be initialized in any order.
Initializers for statics must be evaluatable at compile time.
Members not specified in the initializer list are default initialized.
------
struct X { int a; int b; int c; int d = 7;}
static X x = { a:1, b:2}; // c is set to 0, d to 7
static X z = { c:4, b:5, a:2 , d:5}; // z.a = 2, z.b = 5, z.c = 4, z.d = 5
------
C-style initialization, based on the order of the members in the
struct declaration, is also supported:
------
static X q = { 1, 2 }; // q.a = 1, q.b = 2, q.c = 0, q.d = 7
------
$(P Struct literals can also be used to initialize statics, but
they must be evaluable at compile time.)
-----
static X q = S( 1, 2+3 ); // q.a = 1, q.b = 5, q.c = 0, q.d = 7
-----
$(P The static initializer syntax can also be used to initialize
non-static variables, provided that the member names are not given.
The initializer need not be evaluatable at compile time.)
----
void test(int i)
{
S s = { 1, i }; // q.a = 1, q.b = i, q.c = 0, q.d = 7
}
----
Static Initialization of Unions
Unions are initialized explicitly.
------
union U { int a; double b; }
static U u = { b : 5.0 }; // u.b = 5.0
------
Other members of the union that overlay the initializer,
but occupy more storage, have
the extra storage initialized to zero.
Dynamic Initialization of Structs
$(P Structs can be dynamically initialized from another
value of the same type:)
----
struct S { int a; }
S t; // default initialized
t.a = 3;
S s = t; // s.a is set to 3
----
$(P If $(TT opCall) is overridden for the struct, and the struct
is initialized with a value that is of a different type,
then the $(TT opCall) operator is called:)
----
struct S
{ int a;
static S $(B opCall)(int v)
{ S s;
s.a = v;
return s;
}
static S $(B opCall)(S v)
{ S s;
s.a = v.a + 1;
return s;
}
}
S s = 3; // sets s.a to 3
S t = s; // sets t.a to 3, S.$(B opCall)(s) is not called
----
$(LNAME2 StructLiteral, Struct Literals)
$(P Struct literals consist of the name of the struct followed
by a parenthesized argument list:)
---
struct S { int x; float y; }
int foo(S s) { return s.x; }
foo( S(1, 2) ); // set field x to 1, field y to 2
---
$(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.
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.
If there are anonymous unions in the struct, only the first
member of the anonymous union can be initialized with a
struct literal, and all subsequent non-overlapping fields are default
initialized.
)
Struct Properties
$(TABLE2 Struct Properties,
$(TR $(TD .sizeof) $(TD Size in bytes of struct))
$(TR $(TD .alignof) $(TD Size boundary struct needs to be aligned on))
$(TR $(TD .tupleof) $(TD Gets type tuple of fields))
)
Struct Field Properties
$(TABLE2 Struct Field Properties,
$(TR $(TD .offsetof) $(TD Offset in bytes of field from beginning of struct))
)
$(V2
$(SECTION3 Const and Invariant Structs,
$(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).
)
----
const struct 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; // ok, t.a is now 3
t.a = 4; // error, t.a is const
}
----
)
)
$(V2
$(SECTION3 Struct Constructors,
$(P Struct constructors are used to initialize an instance
of a struct.
The $(I ParameterList) may not be empty.
Struct instances that are not instantiated with a constructor
are default initialized to their $(CODE .init) value.
)
)
)
$(V2
$(SECTION3 Struct Postblits,
$(GRAMMAR
$(GNAME StructPostblit):
$(B this(this)) $(LINK2 function.html#FunctionBody, $(I FunctionBody))
)
$(P $(I Copy construction) is defined as initializing
a struct instance from another struct of the same type.
Copy construction is divided into two parts:)
$(OL
$(LI blitting the fields, i.e. copying the bits)
$(LI running $(I postblit) on the result)
)
$(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:
)
---
struct S
{
int[] a; // array is privately owned by this instance
this(this)
{
a = a.dup;
}
~this()
{
delete a;
}
}
---
)
)
$(V2
$(SECTION3 Struct Destructors,
$(P Destructors are called when an object goes out of scope.
Their purpose is to free up resources owned by the struct
object.
)
)
)
$(V2
$(SECTION3 Assignment Overload,
$(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 one
object over another, already initialized, type:
)
---
struct S { ... }
S s; // default construction of s
S t = s; // t is copy-constructed from s
t = s; // t is assigned from s
---
$(P Struct assignment $(CODE t=s) is defined to be semantically
equivalent to:
)
---
t = S.opAssign(s);
---
$(P where $(CODE opAssign) is a member function of S:)
---
S* opAssign(S s)
{ ... bitcopy *this into tmp ...
... bitcopy s into *this ...
... call destructor on tmp ...
return this;
}
---
$(P While the compiler will generate a default $(CODE opAssign)
as needed, a user-defined one can be supplied. The user-defined
one must still implement the equivalent semantics, but can
be more efficient.
)
$(P One reason a custom $(CODE opAssign) might be more efficient
is if the struct has a reference to a local buffer:
)
---
struct S
{
int[] buf;
int a;
S* opAssign(ref const S s)
{
a = s.a;
return this;
}
this(this)
{
buf = buf.dup;
}
~this()
{
delete buf;
}
}
---
$(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.
)
)
)
$(V2
$(LNAME2 nested, Nested Structs)
$(P A $(I nested struct) is a struct that is declared inside the scope
of a function or a templated struct that has aliases to local
functions as a template argument.
Nested structs have member functions.
It has access to the context of its enclosing scope
(via an added 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
}
---
$(P Nested structs cannot be used as fields or as the
element type of an array:
)
---
void foo()
{ int i = 7;
struct SS
{
int x,y;
int bar() { return x + i + 1; }
}
struct DD
{
SS s; // error, cannot be field
}
SS[3] a; // error, cannot be array element
SS[] a; // error, cannot be array element
}
---
$(P A struct can be prevented from being nested by
using the static attribute, but then of course it
will not be able to access variables from its enclosing
scope.)
---
void foo()
{ int i = 7;
$(B static) struct SS
{
int x,y;
int bar()
{
return i; // error, SS is not a nested struct
}
}
}
---
$(P A templated struct can become a nested struct if it
has a local function passed as an aliased argument:
)
---
struct A(alias F)
{
int fun(int i) { return F(i); }
}
A!(F) makeA(alias F)() {return A!(F)(); }
void main()
{
int x = 40;
int fun(int i) { return x + i; }
A!(fun) a = makeA!(fun)();
a.fun(2);
}
---
)
)
Macros:
TITLE=Structs, Unions
WIKI=Struct
NO=$(TD )
YES=$(TD X)
GLINK=$(LINK2 #$0, $(I $0))
GNAME=$(I $0)
FOO=