he_the_great (he_the_great) wrote,

Explaining Language Design (Part 1: Default Initialization)

Scott Meyers recently gave a talk, "The Last Thing D Needs," which dove into some examples of simple code which make explaining a language harder. This post series will be an attempt to recreate the examples utilizing D, giving explanations of why and to expand on what D does provide. I will not be able to follow the exact same flow as the talk, but it will be close.

This is not an attempt to dig into the dark corners of D.

The talk begins with initialization asking for the value of x1 when it is declared. I'll skip to x2.

int x2; // (at global scope)

It is important to note that D does not have a global scope. Instead this variable is being declared at module scope, and for the purposes of Scott's point this is fine. Another difference from C++ is that this variable is within Thread Local Storage, if it makes you feel better then prepending __gshared will place it in the same location as C++.

C++ will define the value of x2 to be 0 since it is essentially no runtime cost.

Similarly D also defines x2 to be 0. The reason is because variables are always initialized to their init value (T.init). In the case of integers the init value is 0 since there is no value which is invalid. It is common for a programmer to initialize an integer to -1 when desiring a sentinel value. However this is not an invalid value which will cause the program to halt; contrast -1 to the null value for pointers.

int x2prime = void// (at global scope)

D provides a way to state a variable should not be initialized. However this module variable will likely always be initialized to zero due to its low overhead.

int[100] a2; // (at global scope)

C++ and D are still in agreement that the values in a2 are 0.

void main() {
    assert(x2 == 0);
    version(noneassert(x2prime); // possibly zero
    import std.algorithm : all;
    assert(a2[].all!(x => x == 0));

Keeping with tradition, code examples are just a small piece of a larger program. Putting them together, and in order, will provide a runnable program.

The second assert has been versioned out because it is possible that it will fail if the memory happens to be 0

    static int x3;
    assert(x3 == 0);
    static int x3prime = void;
    version(noneassert(x3prime); // possibly zero

Here C++ will once again provide 0 for x3. And D will provide a means to request no initialization.

    int* px = new int;
    assert(*px == 0);

    import core.memory : GC;
    int* pxprime = cast(int*)GC.malloc(int.sizeof);
    version(noneassert(*pxprime); // possibly zero

C++ finally reaches the point where it no longer provides a guarantee on the value. The value will be a valid integer, but it will not be known exactly what. There is a good probability that the pointer will point to a memory location which is 0 resulting in a working program the majority of the time.

D guarantees that px will point to an integer with value 0. It does not provide syntax to initialize a primitive type on the heap, so it is not possible to request the location not be initialized in a straight forward manner.

        int x4;
        assert(x4 == 0);

C++ and D do not change behavior when a new scope is introduced, so C++ is unknown value while D guarantees 0.

    int[100] a1;
    assert(a1[].all!(x => x == 0));

    int[100] a1prime = void;

C++ avoids the runtime cost and does not specify the values for a1. D continues to guarantee initialization to 0 unless requested otherwise.

    static int[100] a3;
    assert(a3[].all!(x => x == 0));

The final item on initialization comes as a no runtime cost for C++ meaning a3 will contain all 0. And D continues its tradition.


D will always initialize variables unless explicitly requesting otherwise. The exact value will depend on the type, for example pointers will be null and floating point will be NAN. For this reason it is still important to initialize the variables in your program, however if it is forgotten the program will behave the same every run it comes across a variable with an implicit initialization.

struct Point {
    int x;
    int y;

    @disable this();

void main() {
    Point p;

What is the value of p?

D provides the ability to disable the default constructor. This is a way to force the user into providing specific initialization. What this means is that the compiler will fail preventing p from ever existing. One of the primary goals of this feature is to provide a NotNull template wrapper, at the time of this writing the NotNull wrapper has not had all the details flushed out.

  1. Part 1: Default Initialization
  2. Part 2: Const Inference
  3. Part 3: Lambdas in C++
  4. Part 4: Lambdas in D
  5. Part 5: Type Inference
  6. Part 6: Inheritance
  7. Part 7: Algorithm Complexity
  8. Part 8: Essential Complexity
Tags: c++, dlang, programming
  • Post a new comment


    default userpic

    Your reply will be screened

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.