
Basic operators
This section is a whirlwind tour of the basic operators D supports. For the most part, things are the same as they are in C. There are a few minor differences that will be highlighted as we come to them. More operators will appear later in this chapter and throughout the book. You can read more about D's operators at http://dlang.org/expression.html.
Arithmetic operators
All of the common arithmetic operators are available: +
, -
, *
, /
and %
, representing addition, subtraction, multiplication, division, and modulus respectively. Additionally, D has an exponentiation operator, ^^
, which raises the left operand to an exponent (power) represented by the right operand. For example, 22 can be expressed as 2 ^^ 2
.
D also supports the standard increment and decrement operators. In the prefix form (++x
and --x
), the result of the expression is the new value. In the postfix form (x++
and x--
), the result is the original value of the operand. To be more explicit, under the hood D is doing something like this for the prefix version:
x = x + 1; return x;
And this for the postfix version:
auto temp = x; x = x + 1; return temp;
In the postfix form, if the resulting value is never used, then the compiler can optimize temp
away and it will effectively be the same as the prefix version. For example:
int x = 2; x++; // Identical to ++x – no temporary
Like C++, D allows the increment and decrement operators to be overloaded by custom types. Unlike C++, D guarantees that the temporary variable in a postfix expression can always be optimized away when it isn't needed, even if the operand is a user-defined type. Even so, it's considered best practice to use the prefix form unless the behavior of the postfix expression is desired.
Bitwise operators
I assume you already know that there are eight bits in a byte, that bits can be 1
or 0
, and that bitwise operators can be used to selectively turn bits on or off, reverse their state, or move them around. D supports the binary bitwise operators &
, |
, and ^
, representing binary AND, OR, and XOR, and the unary operator ~
, representing the one's complement. The left and right shift operators, <<
and >>
, are also supported.
Additionally, D has the unsigned right shift operator, >>>
. Anyone with a Java background will be familiar with this. When the left operand is an unsigned type, >>
and >>>
behave identically. When operating on a signed type, the right shift operator, >>
, preserves the sign bit. This means that right shifting a positive value will yield a positive value and right shifting a negative value produces a negative value. The unsigned right shift operator treats the sign bit as any other bit and does not preserve it. Essentially, it's the same as casting the signed type to an unsigned type and performing a right shift.
int a = -3; writeln(a >> 4); // -1 writeln(a >>> 4); // 268_435_455 writeln(cast(uint)a >> 4); // 268_435_455
You've already learned about the assignment operator, =
. Although the same operator is used for both assignment and initialization, the language does make a distinction when overloading operators on user-defined types. Additionally, all of the binary arithmetic operators and the bitwise operators have compact forms, referred to as opAssign operators, that store the result of the expression in the left operand. These are +=
, -=
, *=
, /=
, %=
, ^^=
, &=
, |=
, ^=
, <<=
, >>=
and >>>=
.
Relational and logical operators
Relational operators determine the relationship between two operands in terms of equality and ordering (greater than and less than). Relational expressions evaluate to true
or false
. D supports the same relational operators found in other C-family languages: ==
, !=
, <
, >
, <=
, >=
, representing equal, not equal, less than, greater than, less than or equal, and greater than or equal. Due to the potential for rounding errors and the existence of special values such as NaN and infinity, floating-point comparisons can be tricky. The Phobos module std.math
provides functions such as isIdentical
, isInfinity
, isNaN,
and approxEquals
to help.
The binary is
operator is similar to the equality operator ==
. More technically, x is y
is referred to as the identity expression. For value types, this is usually the same as x == y
, though this isn't always true for floating point values and struct
instances. For example:
float f; // Initialized to float.nan writeln(f == f); // false writeln(f is f); // true
Instances of a struct
type that has overridden the equality operator will usually cause ==
and is
to produce different results. Otherwise, the default behavior of struct
equality is the same as is
, which is to make a bit-by-bit comparison (struct
s are introduced in Chapter 3, Programming Objects the D Way, and operator overloading in Chapter 5, Generic Programming Made Easy). The difference between ==
and is
becomes most apparent when working with reference types, as we'll observe later.
Logical operators produce true
or false
. For anyone with C-family experience, there is nothing special or surprising about them in D: x && y
evaluates to true
if both operands are true
; x || y
evaluates to true
if either operand is true
; !x
evaluates to true
if the operand is false
.
The cast operator
The cast operator converts a variable from one type to another. It looks like this:
auto y = cast(T)x;
Here, T
represents the type to which x
is cast. If the cast is not legal, the compiler will emit an error. The traditional C-style cast, which is (T)
without the cast
keyword, is not supported. Additionally, D does not support multiple cast operators for different types of casts as C++ does. Though, as we'll see in the next chapter, D's cast
has a special feature when x
is a class instance and T
is a class or interface.