Declarations
Declaration: alias Decl Decl Decl: StorageClasses Decl BasicType Declarators ; BasicType Declarator FunctionBody AutoDeclaration Declarators: DeclaratorInitializer DeclaratorInitializer , DeclaratorIdentifierList DeclaratorInitializer: Declarator Declarator = Initializer DeclaratorIdentifierList: DeclaratorIdentifier DeclaratorIdentifier , DeclaratorIdentifierList DeclaratorIdentifier: Identifier Identifier = Initializer BasicType: BasicTypeX .IdentifierList IdentifierList Typeof Typeof . IdentifierList const( type ) immutable( type ) shared( type ) inout( 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 FunctionAttributesopt function Parameters FunctionAttributesopt Declarator: BasicType2opt Declarator DeclaratorSuffixesopt BasicType2opt Identifier DeclaratorSuffixesopt DeclaratorSuffixes: DeclaratorSuffix DeclaratorSuffix DeclaratorSuffixes DeclaratorSuffix: [ ] [ AssignExpression ] [ Type ] TemplateParameterListopt Parameters MemberFunctionAttributesopt IdentifierList: Identifier Identifier . IdentifierList TemplateInstance TemplateInstance . IdentifierList StorageClasses: StorageClass StorageClass StorageClasses StorageClass: abstract auto const deprecated extern final immutable inout shared nothrow override pure scope static synchronized Type: BasicType BasicType Declarator2 Declarator2: BasicType2 Declarator2 ( Declarator2 ) ( Declarator2 ) DeclaratorSuffixes Parameters: ( ParameterList ) ( ) ParameterList: Parameter Parameter , ParameterList Parameter ... ... Parameter: Declarator Declarator = DefaultInitializerExpression InOut Declarator InOut Declarator = DefaultInitializerExpression InOut: in out ref lazy FunctionAttributes: FunctionAttribute FunctionAttribute FunctionAttributes FunctionAttribute: nothrow pure MemberFunctionAttributes: MemberFunctionAttribute MemberFunctionAttribute MemberFunctionAttributes MemberFunctionAttribute: const immutable inout shared FunctionAttribute DefaultInitializerExpression: AssignExpression __FILE__ __LINE__ Initializer: VoidInitializer NonVoidInitializer NonVoidInitializer: AssignExpression ArrayInitializer StructInitializer ArrayInitializer: [ ] [ ArrayMemberInitializations ] ArrayMemberInitializations: ArrayMemberInitialization ArrayMemberInitialization , ArrayMemberInitialization , ArrayMemberInitializations ArrayMemberInitialization: NonVoidInitializer AssignExpression : NonVoidInitializer StructInitializer: { } { StructMemberInitializers } 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 declarations may be used as an alternative:
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 Identifier = AssignExpression ;
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]
Type Aliasing
It's sometimes convenient to use an alias for a type, such as a shorthand for typing out a long, complex type like a pointer to a function. In D, this is done with the alias declaration:
alias abc.Foo.bar myint;
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 int myint; void foo(int x) { . } void foo(myint m) { . } // error, multiply defined function foo
Alias Declarations
A symbol can be declared as an alias of another symbol. For example:
import string; alias string.strlen mylen; ... int len = mylen("hello"); // actually calls string.strlen()
The following alias declarations are valid:
template Foo2(T) { alias T t; } alias Foo2!(int) t1; alias Foo2!(int).t t2; alias t1.t t3; alias t2 t4; 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 win32.foo myfoo; } version (linux) { alias linux.bar myfoo; }
Aliasing can be used to 'import' a symbol from an import into the current scope:
alias string.strlen 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 B.foo 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 foo.bar abc; // 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 s.i a; // illegal, s.i is an expression alias S.i b; // 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.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 { 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
Where Typeof is most useful is in writing generic template code.
Void Initializations
VoidInitializer: voidNormally, 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; writefln(x); // will print garbage }Therefore, one should only use void initializers as a last resort when optimizing critical code.