Declarations
Declaration:
AliasDeclaration
AggregateDeclaration
EnumDeclaration
ImportDeclaration
Decl
Decl:
StorageClassesopt BasicType Declarators ;
StorageClassesopt BasicType Declarator FunctionBody
AutoDeclaration
Declarators:
DeclaratorInitializer
DeclaratorInitializer , DeclaratorIdentifierList
DeclaratorInitializer:
Declarator
Declarator TemplateParametersopt = Initializer
DeclaratorIdentifierList:
DeclaratorIdentifier
DeclaratorIdentifier , DeclaratorIdentifierList
DeclaratorIdentifier:
Identifier
Identifier TemplateParametersopt = Initializer
BasicType:
BasicTypeX
. IdentifierList
IdentifierList
Typeof
Typeof . IdentifierList
TypeCtor ( Type )
BasicTypeX:
bool
byte
ubyte
short
ushort
int
uint
long
ulong
char
wchar
dchar
float
double
real
ifloat
idouble
ireal
cfloat
cdouble
creal
void
BasicType2:
*
[ ]
[ AssignExpression ]
[ AssignExpression .. AssignExpression ]
[ Type ]
delegate Parameters MemberFunctionAttributesopt
function Parameters FunctionAttributesopt
Declarator:
BasicType2opt ( Declarator ) DeclaratorSuffixesopt
BasicType2opt Identifier DeclaratorSuffixesopt
DeclaratorSuffixes:
DeclaratorSuffix
DeclaratorSuffix DeclaratorSuffixes
DeclaratorSuffix:
[ ]
[ AssignExpression ]
[ Type ]
Parameters MemberFunctionAttributesopt
TemplateParameters Parameters MemberFunctionAttributesopt Constraintopt
IdentifierList:
Identifier
Identifier . IdentifierList
TemplateInstance
TemplateInstance . IdentifierList
StorageClasses:
StorageClass
StorageClass StorageClasses
StorageClass:
LinkageAttribute
AlignAttribute
deprecated
enum
static
extern
abstract
final
override
synchronized
auto
scope
const
immutable
inout
shared
__gshared
Property
nothrow
pure
ref
TypeCtors:
TypeCtor
TypeCtor TypeCtors
TypeCtor:
const
immutable
inout
shared
Type:
TypeCtorsopt BasicType
TypeCtorsopt BasicType Declarator2
Declarator2:
BasicType2opt DeclaratorSuffixesopt
BasicType2opt ( Declarator2 ) DeclaratorSuffixesopt
Parameters:
( ParameterListopt )
ParameterList:
Parameter
Parameter , ParameterList
...
Parameter:
InOutopt BasicType Declarator
InOutopt BasicType Declarator ...
InOutopt BasicType Declarator = DefaultInitializerExpression
InOutopt Type
InOutopt Type ...
InOut:
InOutX
InOut InOutX
InOutX:
auto
TypeCtor
final
in
lazy
out
ref
scope
FunctionAttributes:
FunctionAttribute
FunctionAttribute FunctionAttributes
FunctionAttribute:
nothrow
pure
Property
MemberFunctionAttributes:
MemberFunctionAttribute
MemberFunctionAttribute MemberFunctionAttributes
MemberFunctionAttribute:
const
immutable
inout
shared
FunctionAttribute
DefaultInitializerExpression:
AssignExpression
SpecialKeyword
Initializer:
VoidInitializer
NonVoidInitializer
NonVoidInitializer:
ExpInitializer:
ArrayInitializer
StructInitializer
ExpInitializer:
AssignExpression
ArrayInitializer:
[ ArrayMemberInitializationsopt ]
ArrayMemberInitializations:
ArrayMemberInitialization
ArrayMemberInitialization ,
ArrayMemberInitialization , ArrayMemberInitializations
ArrayMemberInitialization:
NonVoidInitializer
AssignExpression : NonVoidInitializer
StructInitializer:
{ StructMemberInitializersopt }
StructMemberInitializers:
StructMemberInitializer
StructMemberInitializer ,
StructMemberInitializer , StructMemberInitializers
StructMemberInitializer:
NonVoidInitializer
Identifier : NonVoidInitializer
Declaration Syntax
Declaration syntax generally reads right to left:
int x; // x is an int
int* x; // x is a pointer to int
int** x; // x is a pointer to a pointer to int
int[] x; // x is an array of ints
int*[] x; // x is an array of pointers to ints
int[]* x; // x is a pointer to an array of ints
Arrays read right to left as well:
int[3] x; // x is an array of 3 ints
int[3][5] x; // x is an array of 5 arrays of 3 ints
int[3]*[5] x; // x is an array of 5 pointers to arrays of 3 ints
Pointers to functions are declared using the function keyword:
int function(char) x; // x is a pointer to
// a function taking a char argument
// and returning an int
int function(char)[] x; // x is an array of
// pointers to functions
// taking a char argument
// and returning an int
C-style array, function pointer and pointer to array declarations are deprecated:
int x[3]; // x is an array of 3 ints
int x[3][5]; // x is an array of 3 arrays of 5 ints
int (*x[5])[3]; // x is an array of 5 pointers to arrays of 3 ints
int (*x)(char); // x is a pointer to a function taking a char argument
// and returning an int
int (*[] x)(char); // x is an array of pointers to functions
// taking a char argument and returning an int
In a declaration declaring multiple symbols, all the declarations must be of the same type:
int x,y; // x and y are ints
int* x,y; // x and y are pointers to ints
int x,*y; // error, multiple types
int[] x,y; // x and y are arrays of ints
int x[],y; // error, multiple types
Implicit Type Inference
AutoDeclaration:
StorageClasses AutoDeclarationX ;
AutoDeclarationX:
Identifier = TemplateParametersopt Initializer
AutoDeclarationX , Identifier TemplateParametersopt = Initializer
If a declaration starts with a StorageClass and has a NonVoidInitializer from which the type can be inferred, the type on the declaration can be omitted.
static x = 3; // x is type int
auto y = 4u; // y is type uint
auto s = "string"; // s is type immutable(char)[]
class C { ... }
auto c = new C(); // c is a handle to an instance of class C
The NonVoidInitializer cannot contain forward references (this restriction may be removed in the future). The implicitly inferred type is statically bound to the declaration at compile time, not run time.
An ArrayLiteral is inferred to be a dynamic array type rather than a static array:
auto v = ["hello", "world"]; // type is string[], not string[2]
Alias Declarations
AliasDeclaration:
alias StorageClassesopt BasicType Declarator
alias AliasDeclarationX ;
AliasDeclarationX:
Identifier TemplateParametersopt = StorageClassesopt Type
AliasDeclarationX , Identifier TemplateParametersopt = StorageClassesopt Type
AliasDeclarations create a symbol that is an alias for another type, and can be used anywhere that other type may appear.
alias myint = abc.Foo.bar;
Aliased types are semantically identical to the types they are aliased to. The debugger cannot distinguish between them, and there is no difference as far as function overloading is concerned. For example:
alias myint = int;
void foo(int x) { . }
void foo(myint m) { . } // error, multiply defined function foo
A symbol can be declared as an alias of another symbol. For example:
import string;
alias mylen = string.strlen;
...
int len = mylen("hello"); // actually calls string.strlen()
The following alias declarations are valid:
template Foo2(T) { alias t = T; }
alias t1 = Foo2!(int);
alias t2 = Foo2!(int).t;
alias t3 = t1.t;
alias t4 = t2;
t1.t v1; // v1 is type int
t2 v2; // v2 is type int
t3 v3; // v3 is type int
t4 v4; // v4 is type int
Aliased symbols are useful as a shorthand for a long qualified symbol name, or as a way to redirect references from one symbol to another:
version (Win32)
{
alias myfoo = win32.foo;
}
version (linux)
{
alias myfoo = linux.bar;
}
Aliasing can be used to ‘import’ a symbol from an import into the current scope:
alias strlen = string.strlen;
Aliases can also ‘import’ a set of overloaded functions, that can be overloaded with functions in the current scope:
class A {
int foo(int a) { return 1; }
}
class B : A {
int foo( int a, uint b ) { return 2; }
}
class C : B {
int foo( int a ) { return 3; }
alias foo = B.foo;
}
class D : C {
}
void test()
{
D b = new D();
int i;
i = b.foo(1, 2u); // calls B.foo
i = b.foo(1); // calls C.foo
}
Note: Type aliases can sometimes look indistinguishable from alias declarations:
alias abc = foo.bar; // is it a type or a symbol?
The distinction is made in the semantic analysis pass.
Aliases cannot be used for expressions:
struct S { static int i; }
S s;
alias a = s.i; // illegal, s.i is an expression
alias b = S.i; // ok
b = 4; // sets S.i to 4
Extern Declarations
Variable declarations with the storage class extern are not allocated storage within the module. They must be defined in some other object file with a matching name which is then linked in. The primary usefulness of this is to connect with global variable declarations in C files.An extern declaration can optionally be followed by an extern linkage attribute. If there is no linkage attribute it defaults to extern(D):
extern(C) int foo; // variable allocated and initialized in this module with C linkage
extern extern(C) int bar; // variable allocated outside this module with C linkage
// (e.g. in a statically linked C library or another module)
typeof
Typeof:
typeof ( Expression )
typeof ( return )
Typeof is a way to specify a type based on the type of an expression. For example:
void func(int i) {
typeof(i) j; // j is of type int
typeof(3 + 6.0) x; // x is of type double
typeof(1)* p; // p is of type pointer to int
int[typeof(p)] a; // a is of type int[int*]
writefln("%d", typeof('c').sizeof); // prints 1
double c = cast(typeof(1.0))j; // cast j to double
}
Expression is not evaluated, just the type of it is generated:
void func() {
int i = 1;
typeof(++i) j; // j is declared to be an int, i is not incremented
writefln("%d", i); // prints 1
}
There are three special cases:
- typeof(this) will generate the type of what this would be in a non-static member function, even if not in a member function.
- Analogously, typeof(super) will generate the type of what super would be in a non-static member function.
- typeof(return) will, when inside a function scope, give the return type of that function.
class A { }
class B : A {
typeof(this) x; // x is declared to be a B
typeof(super) y; // y is declared to be an A
}
struct C {
static typeof(this) z; // z is declared to be a C
typeof(super) q; // error, no super struct for C
}
typeof(this) r; // error, no enclosing struct or class
If the expression is a Property Function, typeof gives its return type.
struct S {
@property int foo() { return 1; }
}
typeof(S.foo) n; // n is declared to be an int
Where Typeof is most useful is in writing generic template code.
Void Initializations
VoidInitializer:
void
Normally, variables are initialized either with an explicit Initializer or are set to the default value for the type of the variable. If the Initializer is void, however, the variable is not initialized. If its value is used before it is set, undefined program behavior will result.
void foo() {
int x = void;
writeln(x); // will print garbage
}
Therefore, one should only use void initializers as a last resort when optimizing critical code.
Global and Static Initializers
The Initializer for a global or static variable must be evaluatable at compile time. Whether some pointers can be initialized with the addresses of other functions or data is implementation defined. Runtime initialization can be done with static constructors.