Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 227 lines (176 sloc) 6.142 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
Ddoc

$(D_S Rationale,

$(P Questions about the reasons for various design decisions for
D often come up. This addresses many of them.
)

<h2>Operator Overloading</h2>

<h3>Why not name them operator+(), operator*(), etc.?</h3>

$(P This is the way C++ does it, and it is appealing to be able
to refer to overloading $(SINGLEQUOTE +) with $(SINGLEQUOTE operator+). The trouble is
things don't quite fit. For example, there are the
comparison operators <, <=, >, and >=. In C++, all four must
be overloaded to get complete coverage. In D, only a cmp()
function must be defined, and the comparison operations are
derived from that by semantic analysis.
)

$(P Overloading operator/() also provides no symmetric way, as a member
function, to overload the reverse operation. For example,
)

------
class A
{
    int operator/(int i); // overloads (a/i)
    static operator/(int i, A a) // overloads (i/a)
}
------

$(P The second overload does the reverse overload, but
it cannot be virtual, and so has a confusing asymmetry with
the first overload.
)

<h3>Why not allow globally defined operator overload functions?</h3>

$(OL
$(LI Operator overloading can only be done with an argument
as an object, so they logically belong as member functions
of that object. That does leave the case of what to do
when the operands are objects of different types:

------
class A { }
class B { }
int opAdd(class A, class B);
------

Should opAdd() be in class A or B? The obvious stylistic solution
would be to put it in the class of the first operand,

------
class A
{
    int opAdd(class B) { }
}
------
)

$(LI Operator overloads usually need access to private members
of a class, and making them global breaks the object oriented
encapsulation of a class.
)

$(LI (2) can be addressed by operator overloads automatically gaining
"friend" access, but such unusual behavior is at odds with D
being simple.
)

)

<h3>Why not allow user definable operators?</h3>

$(P These can be very useful for attaching new infix operations
to various unicode symbols. The trouble is that in D,
the tokens are supposed to be completely independent of the
semantic analysis. User definable operators would break that.
)

<h3>Why not allow user definable operator precedence?</h3>

$(P The trouble is this affects the syntax analysis, and the syntax
analysis is supposed to be completely independent of the
semantic analysis in D.
)

<h3>Why not use operator names like __add__ and __div__ instead
 of opAdd, opDiv, etc.?</h3>

$(P __ keywords should indicate a proprietary language extension,
not a basic part of the language.
)

<h3>Why not have binary operator overloads be static members, so both
arguments are specified, and there no longer is any issue with the reverse
operations?</h3>

$(P This means that the operator overload cannot be virtual, and
so likely would be implemented as a shell around another
virtual function to do the real work. This will wind up looking
like an ugly hack. Secondly, the opCmp() function is already
an operator overload in Object, it needs to be virtual for several
reasons, and making it asymmetric with the way other operator
overloads are done is unnecessary confusion.
)

<h2>Properties</h2>

<h3>Why does D have properties like T.infinity in the core language to give the
infinity of a floating point type, rather than doing it in a library like C++:
$(CODE std::numeric_limits<T>::infinity)
?</h3>

Let's rephrase that as $(DOUBLEQUOTE if there's a way to express it in the existing
language, why build it in to the core language?)
In regards to T.infinity:

$(OL
$(LI Building it in to the core language means the core language knows
what a floating point infinity is. Being layered in templates, typedefs,
casts, const bit patterns, etc., it doesn't know what it is, and is
unlikely to give sensible error messages if misused.
)

$(LI A side effect of (1) is it is unlikely to be able to use it
effectively in constant folding and other optimizations.
)

$(LI Instantiating templates, loading $(CODE #include) files, etc., all costs
compile time and memory.
)

$(LI The worst, though, is the lengths gone to just to get at infinity,
implying $(DOUBLEQUOTE the language and compiler don't know anything about IEEE 754
floating point - so it cannot be relied on.) And in fact
many otherwise excellent C++ compilers
do not handle NaN's correctly in floating point comparisons.
(Digital Mars C++ does do it correctly.)
C++98 doesn't say anything about NaN or Infinity handling in expressions
or library functions. So it must be assumed it doesn't work.
)

)

$(P To sum up, there's a lot more to supporting NaNs and infinities than
having a template that returns a bit pattern. It has to be built in to
the compiler's core logic, and it has to permeate all the library code
that deals with floating point. And it has to be in the Standard.
)

$(P To illustrate, if either op1 or op2 or both are NaN, then:)

------
(op1 < op2)
------
$(P does not yield the same result as:)
------
!(op1 >= op2)
------
$(P if the NaNs are done correctly.)

<h2>Why use $(D static if(0)) rather than $(D if (0)?)</h2>

    $(P Some limitations are:)

    $(OL
    $(LI if (0) introduces a new scope, static if(...) does not. Why does this
    matter? It matters if one wants to conditionally declare a new variable:

------
static if (...) int x; else long x;
x = 3;
------

    whereas:

------
if (...) int x; else long x;
x = 3; // error, x is not defined
------
    )

    $(LI False static if conditionals don't have to semantically work. For
    example, it may depend on a conditionally compiled declaration somewhere
    else:

------
static if (...) int x;
int test()
{
    static if (...) return x;
    else return 0;
}
------
    )

    $(LI Static if's can appear where only declarations are allowed:

------
class Foo
{
static if (...)
int x;
}
------
    )

    $(LI Static if's can declare new type aliases:

------
static if (0 || is(int T)) T x;
    )
------
    )

)

Macros:
TITLE=Rationale
WIKI=Rationale
CATEGORY_FAQ=$0

Something went wrong with that request. Please try again.