Last update March 7, 2012

Perfect D

Imperfect C++ and more perfect D


Matthew Wilson gives a list of imperfections and constraints of C++ in his book "Imperfect C++ Practical Solutions for Real-Life Programming". I found out Digitalmars D language gives the positive answer of those questions. This paper tries to make them practical.

Note that these comments were probably based on a pre-1.0 version of DMD.


Each item consists of three part, the Wilson's rule, the practice of D, the explanation

Item 1

Imperfection: C++ does not provide direct support for constraints. (p. 4)


    template Constraint(T, U)
         const bool sameType = is(T == U);
         const bool convertable = is(T : U);

const bool structType = is(T == struct) && is(T == struct); const bool classType = is(T == class) && is(T == class);

const bool derived = convertable && !sameType && classType; }

class a { }

class b : a { }

mixin Constraint!(b, a) b_a; mixin Constraint!(a, b) a_b;

static assert(Constraint!(b, a).derived);

Explanation: The most correct answer is compile time reflection, in my opinion. D's built-in compile time constraint almost approaches the goal.

Item 2

Imperfection: C++ does not provide suitable support for postconditions. (p. 15)


    void func()
        assert(input and precondition)
        assert(output and postcondition)
        the function body;

Explanation: DBC is builtin in D.

Wilson suggests:

Recommendation: Use assertions to assert truths about the structure of the code, not about runtime behavior. (p. 21)

In D, using DBC for precondition and postcondition. using assert for runtime behavior and static assert for structure of the code.

Item 3

Imperfection: The C++ standard does not define an ABI, leading to wholesale incompatibilities between many compilers on many platforms. (p. 92)

Answer: D has a partial description of an ABI in the specification (but it's still in progress). See D 1.x / D 2.x

Explain: Walter (the author of D) does a lot work to avoid the incompatibilities between compiler providers. He released the D front end under an open source license. I think each D compiler writer should use it as a reference implementation.

Item 4

Imperfection: C++ is not a suitable module-interface language. (p. 121)

Answer: D has modules.

Item 5

Imperfection: C and C++ say nothing about threading. (p. 132)

Answer: As Java, D has a synchronized keyword.

Item 6

Imperfection: C++ does not provide a mechanism for controlling global object ordering. (p. 158)


       There are two parts to global object initialization, in module and inter module.
       in module:
          module scope static this(){} is a module constructor which initializes all static 
          objects in the module.
       inter module:
          module is instanced by importation order. 

       in module, the static this() constructor is fairly good for initializing. The order of 
       object constructing can be managed well, but the importation order is not as controllable
       as in module global object.

Recommendation: Don't rely on global object initialization ordering. Do utilize global object initialization ordering tracing mechanisms anyway. (p. 161)

Recommendation: Don't create threads during global object initialization. (p. 166)

Item 7

Imperfection: Function-local static instances of classes with nontrivial constructors are not thread-safe. (p. 171)


    Wouldn't this work:
        static className instance=null;
        synchronized { if( instance == null ) { instance = new className(...); } }
    -- MerlinMM?

    instance exists as null reference at program start time (global static program data).  The constructor call only happens at 'new className(...)' which is thread safe due to the global mutex setup by the synchronized keyword.

Should we ask for an extension to the language to allow say: func(...) { synchonized static className instance; } to effectively given us this functionality. Please let me know if I'm missing something. -- MerlinMM?

Item 8

Imperfection: The support for Empty Base Optimization (EBO) flavors is not universal, and varies significantly between compilers. (p. 183)

Item 9

Imperfection: C and C++ are missing a byte type. (p. 191)


D has both byte and ubyte (unsigned byte) type.

Item 10

Imperfection: C and C++ need fixed-sized integer types. (p. 195)


All type sizes are fixed and initial values are defined.

typelength/signalinitial value
bytesigned 8 bits0
ubyteunsigned 8 bits0
shortsigned 16 bits0
ushortunsigned 16 bits0
intsigned 32 bits0
uintunsigned 32 bits0
longsigned 64 bits0L
ulongunsigned 64 bits0L
float32 bit floating pointfloat.nan
double64 bit floating pointdouble.nan

Item 11

Imperfection: C++ needs fixed sized types that are different to, and not implicitly interconvertible with, its built-in integral types. (p. 196)


        import std.stdint; //for typedef int8_t ...

class Serializer { // Operations public: void Write(int8_t i){} void Write(uint8_t i){} void Write(int16_t i){} void Write(uint16_t i){} void Write(int32_t i){} void Write(uint32_t i){} void Write(int64_t i){} void Write(uint64_t i){}


void fn() { auto Serializer s = new Serializer; int8_t i8 = 0; int64_t ui64 = 0; int i = 0;

s.Write(i8); s.Write(ui64); s.Write(i); //Ambiguous call in C++, valid in D s.Write(0); //Ambiguous call in C++, valid in D }

int main() { fn();

return 0; }

Item 12

  • Imperfection:* C and C++ do not provide large fixed-sized integer types. (p. 200)
  • Answer:*
long and ulong is the 64-bit integer type;

Item 13

Imperfection: bool should be the same size as int. (p. 205)

Answer: D has a 1 byte bool type.


Initially D used a bit to represent bool but as per the discussion Boolean != Bit, D has bool.

Item 14

Imperfection: C and C++ do not provide a dimensionof() operator (for array types). (p. 207)

Answer: array.length is the dimensionof() operator.

        int[] a = ...;
        a.length; // return dimension of a;

Item 15

Imperfection: Inability to distinguish between arrays and pointers can result in static array size determination being applied to pointers or class types, yielding incorrect results. (p. 211)


Array and pointer are different types.

         int* p;
         int[] a;

p = a; // ok a = p; // fail a = p[0 .. 3]; //specify dimension.

static if(is(typeof(a) U == U[])) { static assert( is(U == int)); }

static if(is(typeof(a) U == U*)) //fail { static assert(0); }

static if(is(typeof(p) U == U*)) { static assert( is(U == int)); }

static if(is(typeof(p) U == U[]))//fail { static assert(0); }


Item 16

Imperfection: C and C++ arrays decay into pointers when passed to functions. (p. 214)


       void func(int[] b);
       void func(int* b);

These are overloaded functions;

Item 17

Imperfection: C++'s array/pointer duality, combined with its support for polymorphic handling of inherited types, is a hazard against which the compiler provides no assistance. (p. 218)


D is good here!

Item 18

Imperfection: C++ does not support multi-dimensional arrays. (p. 224)

    double[3][3] matrix; //Rectangular? Array;    (big array with two dimensional access)
    double[][]   matrix; //two dimensional Array;(array of array)

Item 19

Imperfection: C++ needs a null keyword, which can be assigned to, and equality compared with, any pointer type, and with no non-pointer types. (p. 229)

Answer: Yes, null is a keyword in D, and it can initialize no primary type.


Item 20

Imperfection: Having more than one discrete value for the Boolean type is dangerous. (p. 236)


        D has a bool type for true and false;
        int  which used as boolean type in D, evaluated zero as false, and no-zero for true;

Item 21

Imperfection: Specification of the size of integer types as implementation dependent in C/C++ reduces the portability of integer literals. (p. 238)


        integer literals evaluated as its smallest fixed-size integer;

Item 22

Imperfection: C++ compilers do not agree on the syntax for suffixes of integer literals larger than those that will fit into (unsigned) long. (p. 239)


        this is error at debug mode;

Item 23

Constraint: C and C++ do not guarantee to ensure that identical string literals will be folded within a single link unit, and cannot do so across separate link units. (p. 242)


        I know nothing about this in D.

Item 24

Constraint: Casting away constness of a constant object is not legal, and will likely lead to unintended consequences. (p. 245)


        D allows both const and immutable to be cast away, this is because casting circumvents the type system, D just hopes you know what you are doing.

Item 25

Constraint: Constants of class type are evaluated as immutable global objects, rather than as compile-time constants. Failure to be mindful of this distinction is hazardous. (p. 246)


        constants is the compile time constants.

Recommendation: Avoid using enum values larger than those that will fit in an int. (p. 248)

Item 26

Imperfection: C++ does not support member constants of floating-point type. (p. 249)


       no such constants;

Item 27

Constraint: C++ does not support member constants of class type. (p. 250)


       no such constants;

Recommendation: Never attempt to simulate class-type member constants with function local static objects. (p. 251)

Item 28

Imperfection: C++ does not provide access control between types involved in a composition relationship. (p. 257)


        package is the access modifer, for module scope public;

Item 29

Imperfection: The introduction of new keywords into the language requires contingency mechanisms

       to maintain portability. (p. 268)

        Maybe later, but not now;   

Item 30

Imperfection: Abuse of C++'s last-known access specifier mechanism leads to code that is hard to use and hard to maintain. (p. 271)


            void member();

public void member();

public{ void member(); ... }

all above is legal, which would you like? go ahead.

Item 31

Imperfection: Implicitly interpreting non-Boolean (sub-)expressions leads to failure to be mindful of the values of certain types, with a consequent propensity to over-engineer user-defined types. (p. 274)


Item 32

Imperfection: The implicit interpretation of scalar types as Boolean in conditional statements facilitates erroneous assignment caused by syntax errors. (p. 275)

Item 33

Imperfection: The contradiction between the old and new for-statement rules results in non-portable code. (p. 278)

Item 34

Imperfection: for statements requiring initializers of two or more types render the new for-scoping rule irrelevant. (p. 279)

Item 35

Imperfection: C++ provides no type-safety for the use of conceptual defined types, except where such types have incompatible underlying types. (p. 294)

Item 36

Imperfection: C++ does not support overloading on conceptually defined types, except where such types happen to have different underlying types on a particular platform. (p. 294)


Recommendation: Avoid making conceptual typedefs that are comprised of contextual typedefs. (p. 304)

Item 37

Imperfection: Logically related types in C++ can, and usually do, have incompatible interfaces and operations, rendering a generalized approach to type manipulation sometimes difficult, and often impossible. (p. 341)


        opCmp is only relationship operator overloading.
        opEqual is only identification operator overloading.

Item 38

Imperfection: The C++ template mechanism instantiates a template based on the arguments to it, and takes no account of how, or in what form, those arguments are subsequently used within the templates. This can lead to the generation of inefficient and/or erroneous code since temporary instances of class type may be created during the forwarding of arguments through the template. (p. 387)


        Maybe true or no true.        

Item 39

Imperfection: Overloading operator &() breaks encapsulation. (p. 425)


        operator & can overload.

Item 40

Imperfection: Provision of implicit conversion operator(s) to pointer type(s) along with subscript operator(s) is non-portable. (p. 434)

Item 41

Imperfection: Some C++ compilers will substitute prefix increment/decrement overloaded operators at the call site in place of absent postfix equivalents. (p.440)



Item 42

Imperfection: Overloading the && and || operators for class types represents an invisible breaking of short-circuit evaluation. (p. 454)

        && and || can not overload.

Item 43

Imperfection: C and C++ make you choose between performance and flexibility when allocating memory. (p. 476)


D has choosen a little of both for you. You can allocate items on stack with scope keyword. You can define your own class allocators if you like malloc/free, but by default you get garbage collector which adds flexibility (e.g. dynamic arrays).

Item 44

Imperfection: C/C++ does not support dynamically dimensioned multidimensional arrays. (p. 491)


Recommendation: An array size shim should always be used when determining the sizes of arrays, as all other approaches do not generalize for both built-in and user defined array types. (p. 505)

Item 45

Imperfection: C++ does not support local functor classes (to be used with template algorithms). (p. 514)


        local class is there.

Item 46

Imperfection: C++ does not provide properties. (p. 531)


        built-in properties:

int.sizeof yields 4 float.nan yields the floating point nan (Not A Number) value (float).nan yields the floating point nan value (3).sizeof yields 4 (because 3 is an int) 2.sizeof syntax error, since "2." is a floating point number int.init default initializer for int'

user properties is easy:

struct Foo { int data() { return m_data; }// read property

int data(int value) { return m_data = value; } // write property

private: int m_data; }

int test() { Foo f; = 3;// same as; return + 3;// same as return + 3; }

FrontPage | News | TestPage | MessageBoard | Search | Contributors | Folders | Index | Help | Preferences | Edit

Edit text of this page (date of last change: March 7, 2012 20:35 (diff))