| 1 |
Ddoc |
|---|
| 2 |
|
|---|
| 3 |
$(D_S Rationale, |
|---|
| 4 |
|
|---|
| 5 |
$(P Questions about the reasons for various design decisions for |
|---|
| 6 |
D often come up. This addresses many of them. |
|---|
| 7 |
) |
|---|
| 8 |
|
|---|
| 9 |
<h2>Operator Overloading</h2> |
|---|
| 10 |
|
|---|
| 11 |
<h3>Why not name them operator+(), operator*(), etc.?</h3> |
|---|
| 12 |
|
|---|
| 13 |
$(P This is the way C++ does it, and it is appealing to be able |
|---|
| 14 |
to refer to overloading $(SINGLEQUOTE +) with $(SINGLEQUOTE operator+). The trouble is |
|---|
| 15 |
things don't quite fit. For example, there are the |
|---|
| 16 |
comparison operators <, <=, >, and >=. In C++, all four must |
|---|
| 17 |
be overloaded to get complete coverage. In D, only a cmp() |
|---|
| 18 |
function must be defined, and the comparison operations are |
|---|
| 19 |
derived from that by semantic analysis. |
|---|
| 20 |
) |
|---|
| 21 |
|
|---|
| 22 |
$(P Overloading operator/() also provides no symmetric way, as a member |
|---|
| 23 |
function, to overload the reverse operation. For example, |
|---|
| 24 |
) |
|---|
| 25 |
|
|---|
| 26 |
------ |
|---|
| 27 |
class A |
|---|
| 28 |
{ |
|---|
| 29 |
int operator/(int i); // overloads (a/i) |
|---|
| 30 |
static operator/(int i, A a) // overloads (i/a) |
|---|
| 31 |
} |
|---|
| 32 |
------ |
|---|
| 33 |
|
|---|
| 34 |
$(P The second overload does the reverse overload, but |
|---|
| 35 |
it cannot be virtual, and so has a confusing asymmetry with |
|---|
| 36 |
the first overload. |
|---|
| 37 |
) |
|---|
| 38 |
|
|---|
| 39 |
<h3>Why not allow globally defined operator overload functions?</h3> |
|---|
| 40 |
|
|---|
| 41 |
$(OL |
|---|
| 42 |
$(LI Operator overloading can only be done with an argument |
|---|
| 43 |
as an object, so they logically belong as member functions |
|---|
| 44 |
of that object. That does leave the case of what to do |
|---|
| 45 |
when the operands are objects of different types: |
|---|
| 46 |
|
|---|
| 47 |
------ |
|---|
| 48 |
class A { } |
|---|
| 49 |
class B { } |
|---|
| 50 |
int opAdd(class A, class B); |
|---|
| 51 |
------ |
|---|
| 52 |
|
|---|
| 53 |
Should opAdd() be in class A or B? The obvious stylistic solution |
|---|
| 54 |
would be to put it in the class of the first operand, |
|---|
| 55 |
|
|---|
| 56 |
------ |
|---|
| 57 |
class A |
|---|
| 58 |
{ |
|---|
| 59 |
int opAdd(class B) { } |
|---|
| 60 |
} |
|---|
| 61 |
------ |
|---|
| 62 |
) |
|---|
| 63 |
|
|---|
| 64 |
$(LI Operator overloads usually need access to private members |
|---|
| 65 |
of a class, and making them global breaks the object oriented |
|---|
| 66 |
encapsulation of a class. |
|---|
| 67 |
) |
|---|
| 68 |
|
|---|
| 69 |
$(LI (2) can be addressed by operator overloads automatically gaining |
|---|
| 70 |
"friend" access, but such unusual behavior is at odds with D |
|---|
| 71 |
being simple. |
|---|
| 72 |
) |
|---|
| 73 |
|
|---|
| 74 |
) |
|---|
| 75 |
|
|---|
| 76 |
<h3>Why not allow user definable operators?</h3> |
|---|
| 77 |
|
|---|
| 78 |
$(P These can be very useful for attaching new infix operations |
|---|
| 79 |
to various unicode symbols. The trouble is that in D, |
|---|
| 80 |
the tokens are supposed to be completely independent of the |
|---|
| 81 |
semantic analysis. User definable operators would break that. |
|---|
| 82 |
) |
|---|
| 83 |
|
|---|
| 84 |
<h3>Why not allow user definable operator precedence?</h3> |
|---|
| 85 |
|
|---|
| 86 |
$(P The trouble is this affects the syntax analysis, and the syntax |
|---|
| 87 |
analysis is supposed to be completely independent of the |
|---|
| 88 |
semantic analysis in D. |
|---|
| 89 |
) |
|---|
| 90 |
|
|---|
| 91 |
<h3>Why not use operator names like __add__ and __div__ instead |
|---|
| 92 |
of opAdd, opDiv, etc.?</h3> |
|---|
| 93 |
|
|---|
| 94 |
$(P __ keywords should indicate a proprietary language extension, |
|---|
| 95 |
not a basic part of the language. |
|---|
| 96 |
) |
|---|
| 97 |
|
|---|
| 98 |
<h3>Why not have binary operator overloads be static members, so both |
|---|
| 99 |
arguments are specified, and there no longer is any issue with the reverse |
|---|
| 100 |
operations?</h3> |
|---|
| 101 |
|
|---|
| 102 |
$(P This means that the operator overload cannot be virtual, and |
|---|
| 103 |
so likely would be implemented as a shell around another |
|---|
| 104 |
virtual function to do the real work. This will wind up looking |
|---|
| 105 |
like an ugly hack. Secondly, the opCmp() function is already |
|---|
| 106 |
an operator overload in Object, it needs to be virtual for several |
|---|
| 107 |
reasons, and making it asymmetric with the way other operator |
|---|
| 108 |
overloads are done is unnecessary confusion. |
|---|
| 109 |
) |
|---|
| 110 |
|
|---|
| 111 |
<h2>Properties</h2> |
|---|
| 112 |
|
|---|
| 113 |
<h3>Why does D have properties like T.infinity in the core language to give the |
|---|
| 114 |
infinity of a floating point type, rather than doing it in a library like C++: |
|---|
| 115 |
$(CODE std::numeric_limits<T>::infinity) |
|---|
| 116 |
?</h3> |
|---|
| 117 |
|
|---|
| 118 |
Let's rephrase that as $(DOUBLEQUOTE if there's a way to express it in the existing |
|---|
| 119 |
language, why build it in to the core language?) |
|---|
| 120 |
In regards to T.infinity: |
|---|
| 121 |
|
|---|
| 122 |
$(OL |
|---|
| 123 |
$(LI Building it in to the core language means the core language knows |
|---|
| 124 |
what a floating point infinity is. Being layered in templates, typedefs, |
|---|
| 125 |
casts, const bit patterns, etc., it doesn't know what it is, and is |
|---|
| 126 |
unlikely to give sensible error messages if misused. |
|---|
| 127 |
) |
|---|
| 128 |
|
|---|
| 129 |
$(LI A side effect of (1) is it is unlikely to be able to use it |
|---|
| 130 |
effectively in constant folding and other optimizations. |
|---|
| 131 |
) |
|---|
| 132 |
|
|---|
| 133 |
$(LI Instantiating templates, loading $(CODE #include) files, etc., all costs |
|---|
| 134 |
compile time and memory. |
|---|
| 135 |
) |
|---|
| 136 |
|
|---|
| 137 |
$(LI The worst, though, is the lengths gone to just to get at infinity, |
|---|
| 138 |
implying $(DOUBLEQUOTE the language and compiler don't know anything about IEEE 754 |
|---|
| 139 |
floating point - so it cannot be relied on.) And in fact |
|---|
| 140 |
many otherwise excellent C++ compilers |
|---|
| 141 |
do not handle NaN's correctly in floating point comparisons. |
|---|
| 142 |
(Digital Mars C++ does do it correctly.) |
|---|
| 143 |
C++98 doesn't say anything about NaN or Infinity handling in expressions |
|---|
| 144 |
or library functions. So it must be assumed it doesn't work. |
|---|
| 145 |
) |
|---|
| 146 |
|
|---|
| 147 |
) |
|---|
| 148 |
|
|---|
| 149 |
$(P To sum up, there's a lot more to supporting NaNs and infinities than |
|---|
| 150 |
having a template that returns a bit pattern. It has to be built in to |
|---|
| 151 |
the compiler's core logic, and it has to permeate all the library code |
|---|
| 152 |
that deals with floating point. And it has to be in the Standard. |
|---|
| 153 |
) |
|---|
| 154 |
|
|---|
| 155 |
$(P To illustrate, if either op1 or op2 or both are NaN, then:) |
|---|
| 156 |
|
|---|
| 157 |
------ |
|---|
| 158 |
(op1 < op2) |
|---|
| 159 |
------ |
|---|
| 160 |
$(P does not yield the same result as:) |
|---|
| 161 |
------ |
|---|
| 162 |
!(op1 >= op2) |
|---|
| 163 |
------ |
|---|
| 164 |
$(P if the NaNs are done correctly.) |
|---|
| 165 |
|
|---|
| 166 |
<h2>Why use $(TT static if(0)) rather than $(TT if (0)?)</h2> |
|---|
| 167 |
|
|---|
| 168 |
$(P Some limitations are:) |
|---|
| 169 |
|
|---|
| 170 |
$(OL |
|---|
| 171 |
$(LI if (0) introduces a new scope, static if(...) does not. Why does this |
|---|
| 172 |
matter? It matters if one wants to conditionally declare a new variable: |
|---|
| 173 |
|
|---|
| 174 |
------ |
|---|
| 175 |
static if (...) int x; else long x; |
|---|
| 176 |
x = 3; |
|---|
| 177 |
------ |
|---|
| 178 |
|
|---|
| 179 |
whereas: |
|---|
| 180 |
|
|---|
| 181 |
------ |
|---|
| 182 |
if (...) int x; else long x; |
|---|
| 183 |
x = 3; // error, x is not defined |
|---|
| 184 |
------ |
|---|
| 185 |
) |
|---|
| 186 |
|
|---|
| 187 |
$(LI False static if conditionals don't have to semantically work. For |
|---|
| 188 |
example, it may depend on a conditionally compiled declaration somewhere |
|---|
| 189 |
else: |
|---|
| 190 |
|
|---|
| 191 |
------ |
|---|
| 192 |
static if (...) int x; |
|---|
| 193 |
int test() |
|---|
| 194 |
{ |
|---|
| 195 |
static if (...) return x; |
|---|
| 196 |
else return 0; |
|---|
| 197 |
} |
|---|
| 198 |
------ |
|---|
| 199 |
) |
|---|
| 200 |
|
|---|
| 201 |
$(LI Static if's can appear where only declarations are allowed: |
|---|
| 202 |
|
|---|
| 203 |
------ |
|---|
| 204 |
class Foo |
|---|
| 205 |
{ |
|---|
| 206 |
static if (...) |
|---|
| 207 |
int x; |
|---|
| 208 |
} |
|---|
| 209 |
------ |
|---|
| 210 |
) |
|---|
| 211 |
|
|---|
| 212 |
$(LI Static if's can declare new type aliases: |
|---|
| 213 |
|
|---|
| 214 |
------ |
|---|
| 215 |
static if (0 || is(int T)) T x; |
|---|
| 216 |
) |
|---|
| 217 |
------ |
|---|
| 218 |
) |
|---|
| 219 |
|
|---|
| 220 |
) |
|---|
| 221 |
|
|---|
| 222 |
Macros: |
|---|
| 223 |
TITLE=Rationale |
|---|
| 224 |
WIKI=Rationale |
|---|