D Programming Language
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 using local clone. Page wiki View or edit the community-maintained wiki page associated with this page.

# Declarations

    Declaration:
        FuncDeclaration
        VarDeclarations
        AliasDeclaration
        AggregateDeclaration
        EnumDeclaration
        ImportDeclaration

    VarDeclarations:
        StorageClassesopt BasicType Declarators ;
        AutoDeclaration

    Declarators:
        DeclaratorInitializer
        DeclaratorInitializer , DeclaratorIdentifierList

    DeclaratorInitializer:
        VarDeclarator
        VarDeclarator TemplateParametersopt = Initializer
        AltDeclarator
        AltDeclarator = Initializer

    DeclaratorIdentifierList:
        DeclaratorIdentifier
        DeclaratorIdentifier , DeclaratorIdentifierList

    DeclaratorIdentifier:
        VarDeclaratorIdentifier
        AltDeclaratorIdentifier

    VarDeclaratorIdentifier:
        Identifier
        Identifier TemplateParametersopt = Initializer

    AltDeclaratorIdentifier:
        BasicType2 Identifier AltDeclaratorSuffixesopt
        BasicType2 Identifier AltDeclaratorSuffixesopt = Initializer
        BasicType2opt Identifier AltDeclaratorSuffixes
        BasicType2opt Identifier AltDeclaratorSuffixes = Initializer

    Declarator:
        VarDeclarator
        AltDeclarator

    VarDeclarator:
        BasicType2opt Identifier

    AltDeclarator:
        BasicType2opt Identifier AltDeclaratorSuffixes
        BasicType2opt ( AltDeclaratorX )
        BasicType2opt ( AltDeclaratorX ) AltFuncDeclaratorSuffix
        BasicType2opt ( AltDeclaratorX ) AltDeclaratorSuffixes

    AltDeclaratorX:
        BasicType2opt Identifier
        BasicType2opt Identifier AltFuncDeclaratorSuffix
        AltDeclarator

    AltDeclaratorSuffixes:
        AltDeclaratorSuffix
        AltDeclaratorSuffix AltDeclaratorSuffixes

    AltDeclaratorSuffix:
        [ ]
        [ AssignExpression ]
        [ Type ]

    AltFuncDeclaratorSuffix:
        Parameters MemberFunctionAttributesopt

    Type:
        TypeCtorsopt BasicType BasicType2opt

    TypeCtors:
        TypeCtor
        TypeCtor TypeCtors

    TypeCtor:
        const
        immutable
        inout
        shared

    BasicType:
        BasicTypeX
        . IdentifierList
        IdentifierList
        Typeof
        Typeof . IdentifierList
        TypeCtor ( Type )
        TypeVector

    BasicTypeX:
        bool
        byte
        ubyte
        short
        ushort
        int
        uint
        long
        ulong
        char
        wchar
        dchar
        float
        double
        real
        ifloat
        idouble
        ireal
        cfloat
        cdouble
        creal
        void

    BasicType2:
        BasicType2X BasicType2opt

    BasicType2X:
        *
        [ ]
        [ AssignExpression ]
        [ AssignExpression .. AssignExpression ]
        [ Type ]
        delegate Parameters MemberFunctionAttributesopt
        function Parameters FunctionAttributesopt

    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

    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 Declarators ;
        alias StorageClassesopt BasicType FuncDeclarator ;
        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:

1. typeof(this) will generate the type of what this would be in a non-static member function, even if not in a member function.
2. Analogously, typeof(super) will generate the type of what super would be in a non-static member function.
3. 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.

### Type Qualifiers vs. Storage Classes

D draws a distinction between a type qualifer and a storage class.

A type qualifier creates a derived type from an existing base type, and the resulting type may be used to create multiple instances of that type.

For example, the immutable type qualifier can be used to create variables of immutable type, such as:

    immutable(int)   x; // typeof(x) == immutable(int)
    immutable(int)[] y; // typeof(y) == immutable(int)[]
                        // typeof(y[0]) == immutable(int)

    // Type constructors create new types that can be aliased:
    alias ImmutableInt = immutable(int);
    ImmutableInt z;     // typeof(z) == immutable(int)

A storage class, on the other hand, does not create a new type, but describes only the type of storage used by the variable or function being declared. For example, a member function can be declared with the const storage class to indicate that it does not modify its implicit this argument:

    struct S
    {
        int x;
        int method() const
        {
    	//x++;    // Error: this method is const and cannot modify this.x
            return x; // OK: we can still read this.x
        }
    }

Although some keywords can be used both as a type qualifier and a storage class, there are some storage classes that cannot be used to construct new types. One example is ref:

    // ref declares the parameter x to be passed by reference
    void func(ref int x)
    {
        x++; // so modifications to x will be visible in the caller
    }

    void main()
    {
        auto x = 1;
        func(x);
        assert(x == 2);

        // However, ref is not a type qualifier, so the following is illegal:
        ref(int) y; // Error: ref is not a type qualifier.
    }

    // Functions can also be declared as 'ref', meaning their return value is
    // passed by reference:
    ref int func2()
    {
        static int y = 0;
        return y;
    }

    void main()
    {
        func2() = 2; // The return value of func2() can be modified.
        assert(func2() == 2);

        // However, the reference returned by func2() does not propagate to
        // variables, because the 'ref' only applies to the return value itself,
        // not to any subsequent variable created from it:
        auto x = func2();
        static assert(typeof(x) == int); // N.B.: *not* ref(int);
                                         // there is no such type as ref(int).
        x++;
        assert(x == 3);
        assert(func2() == 2); // x is not a reference to what func2() returned; it
                              // does not inherit the ref storage class from func2().
    }

Due to the fact that some keywords, such as const, can be used both as a type qualifier and a storage class, it may sometimes result in ambiguous-looking code:

    struct S
    {
        // Is const here a type qualifier or a storage class?
        // Is the return value const(int), or is this a const function that returns
        // (mutable) int?
        const int func() { return 1; }
    }

To avoid such confusion, it is recommended that type qualifier syntax with parentheses always be used for return types, and that function storage classes be written on the right-hand side of the declaration instead of the left-hand side where it may be visually confused with the return type:

    struct S
    {
        // Now it is clear that the 'const' here applies to the return type:
        const(int) func1() { return 1; }

        // And it is clear that the 'const' here applies to the function:
        int func2() const { return 1; }
    }

Copyright © 1999-2015 by Digital Mars ®, All Rights Reserved | Page generated by Ddoc on Tue Mar 24 14:30:25 2015