Because D was originally created by a C++ compiler writer, Walter Bright, it’s an easy language for C and C++ programmers to learn, but there are little differences in the way declarations work. I learned them piecemeal in different places, but I’m going to dump a bunch in this one post.
If you want to declare a pointer in C, both of the following work:
Some people prefer the second form because it puts all the type information to one side. At least, that’s what it looks like. Trouble is, you can fall into this trap:
“Type information on the left” isn’t really how C works. D, on the other hand, does put all the type information to the left, so this works the way it appears:
D also accepts the
char *p syntax, but the rule I go by is
char *p when writing C, and
char* p when writing D, just because that matches how the languages
actually work, so no gotchas.
Digression: how C declarations work
This isn’t about D, but helps to make sense of the subtler differences between C and D declarations.
C declarations are implicit about types.
char *p doesn’t
really say, “
p is of type
char*”; it says “the type of
p is such that
*p evaluates to a
There’s a kind of theoretical elegance to this implicit approach, but 1) it’s backwards and makes complex types
confusing, 2) the theoretical elegance only goes so far because everything’s a special case. For example,
int a; declares an array
a, but makes the expression
a undefined. You can only use certain operations, so
int 2*a; doesn’t work, and neither does
double 1.0 + sin(x);. The expression
4[a] is equivalent to
a, but you can’t declare an array with
int 4[a];. C++ gave up on the theory when it introduced reference syntax
D has a special
function keyword for declaring function
pointers using the “type information on the left” approach. It makes the declaration of function pointers use the same
syntax as the declaration of a function:
Note that the
& is required to get the address of
a function in D (unlike in C and C++). If you want to have an array of pointers, you just add
 to the end of the type, just like you do with any other type.
Similarly for making pointers to types:
Here’s the C equivalent for comparison:
It’s rare to need these complicated types, but the logic for the D declarations is much simpler.
There’s also the
delegate keyword, which works in exactly
the same way for “fat function pointers”.
The most obvious difference from C is that D uses the “type information on the left” approach:
Another difference is in the order of indices for multidimensional arrays. E.g., this C code:
translates to this in D:
Here’s the rule for understanding the D ordering:
T represents a type, then
T is always an array of 4
Ts. Sounds obvious, but it means that if
int must be an array of 4
auto as a storage class keyword since the early
days, but it got mostly forgotten because it’s only allowed in the one place it’s the default, anyway. (It effectively
means “this variable goes on the stack”.) C++ repurposed the keyword to enable automatic type deduction.
You can also use
auto with automatic type deduction in D,
but it’s not actually required. Type deduction is always enabled in D; you just need to make your declaration
unambiguously a declaration. For example, these work in D (but not all in C++):
No need for forward declarations at global scope
This code works:
Similarly for structs and classes. Order of definition doesn’t matter, and forward declarations aren’t required.
Order does matter in local scope, though:
Either the definition of
bar() needs to be put before its
bar() needs a forward declaration.
const keyword in C declarations can be confusing.
const int *p vs
int const *p vs
int const *p.) D supports the same syntax, but also allows
const with parentheses:
const is transitive in D, anyway, and this syntax makes it much
clearer. The same parenthetical syntax works with
too. Although C-style syntax is supported by D, I always prefer the parenthetical style for a few more reasons.
ref is the D alternative to C++’s references. In D,
ref doesn’t create a new type, it just controls how the
instance of the type is stored in memory (i.e, it’s a storage class). C++ acts as if references are types, but
references have so many special restrictions that they’re effectively like a complex version of a storage class (in
Walter’s words, C++ references try to be both a floor wax and dessert topping). For example, C++ treats
int& like a type, but forbids declaring an array of
As a former C++ programmer, I used to write D function arguments like this:
Now I write them like this:
The difference becomes more obvious with more complex types. Treating
ref like a storage class ends up being cleaner because that’s the way it
actually is in D.
ref is only supported with function arguments or
foreach loop variables, so you can’t declare a regular local
variable to be
D’s backward-compatible support for the C-style
keyword creates an unfortunate gotcha:
foo() doesn’t return a
const int*. The
const applies to the
foo() member function itself, meaning that it works on
const instances of
S and returns a (non-
avoid that trap, I always use the D-style
const() syntax, and
write member function qualifiers on the right:
C++ allows initialising struct and class instances without an
This syntax famously leads to ambiguities with function declaration syntax in special cases (Scott Meyers’ “most
vexing parse”). People like Herb Sutter have written
enough about it. D only supports initialisation with
C syntax has some weird corners, too. Here’s a simple one:
That looks like a useless multiplication between two variables, but logically it could be a declaration of
y as a pointer to a type
x. Expression and declaration are totally different parses that depend on
what the symbol
x means in this scope. (Even worse, if it’s a
declaration, then the new
y could shadow an existing
y, which could affect later parses.) So C compilers need to
track symbols in a symbol table while parsing, which is why C has forward declarations in practice.
D sidesteps the ambiguity by requiring a typecast to
if you really want to write an arithmetic expression without assigning it to anything:
I’ve never seen useful code do that, but that rule helps D parse simply without forward declarations.
Here’s another quirk of C syntax. Remember that C declarations work by having a basic type on the left, followed by expressions that evaluate to that type? C allows parentheses in those expressions, and doesn’t care about whitespace as long as symbols don’t run together. That means these two declarations are equivalent:
But what if, instead of
int, we use some symbol that might
be a typedef?
Just for fun, we can exploit shadowing and C’s archaic type rules:
The first line makes
x a typedef to a function pointer
type. The first
x to be a function pointer variable, shadowing the typedef. The second
x(x); is a function call that passes
x as an argument. Yes, this code actually compiles, but it’s undefined
behaviour because the function pointer is dereferenced without being initialised.
D avoids this chaos thanks to its “all type information on the left” rule. There’s no need to put parentheses around
symbols in declarations, so
x(y); is always a function