Sobrecarga de Operadores
A sobrecarga é realizada interpretando funções membro de estruturas e classes nomeadas especialmente como sendo implementações de operadores unários e binários. Nenhuma sintaxe adicional é usada.Sobrecarga de Operadores Unários
op | opfunc |
---|---|
-e | opNeg |
+e | opPos |
~e | opCom |
e++ | opPostInc |
e-- | opPostDec |
cast(tipo)e | opCast |
Dado um operador unário sobrecarregável op e sua função membro correspondente opfunc, a sintaxe:
op aonde a é uma objeto de referência de uma classe ou estrutura, é interpretado como se fosse escrito como:
a.opfunc()
Sobrecarregando ++e e --e
Já que ++e é definido como sendo semanticamente equivalente à (e += 1), a expressão ++e é reescrita como (e += 1), e então verificação pela sobrecarga de operador é feita. A situação é análoga para --e.Exemplos
-
class A { int opNeg(); }
A a;
-a; // equialente à a.opNeg(); -
class A { int opNeg(int i); }
A a;
-a; // equivalente à a.opNeg(), que é um erro
Sobrecarregando cast(tipo)e
A função membro e.opCast() é chamada, e o valor de retorno de opCast() é implicitamente convertido para tipo. Já que funções não podem ser sobrecarregadas baseadas em um valor de retorno, pode haver apenas um opCast por estrutura ou classe. Sobrecarregar o operador de moldagem não afeta moldagens implicitas, ele só se aplica à moldagens explícitas.struct A
{
int opCast() { return 28; }
}
void test()
{
A a;
long i = cast(long)a; // i é ajustado para 28L
void* p = cast(void*)a; // erro, não pode converter
// implicitamente int para void*
int j = a; // erro, não pode converter implicitamente
// A para int
}
Sobrecarga de Operadores Binários
op | comutativo? | opfunc | opfunc_r |
---|---|---|---|
+ | sim | opAdd | opAdd_r |
- | não | opSub | opSub_r |
* | sim | opMul | opMul_r |
/ | não | opDiv | opDiv_r |
% | não | opMod | opMod_r |
& | sim | opAnd | opAnd_r |
| | sim | opOr | opOr_r |
^ | sim | opXor | opXor_r |
<< | não | opShl | opShl_r |
>> | não | opShr | opShr_r |
>>> | não | opUShr | opUShr_r |
~ | não | opCat | opCat_r |
== | sim | opEquals | - |
!= | sim | opEquals | - |
< | sim | opCmp | - |
<= | sim | opCmp | - |
> | sim | opCmp | - |
>= | sim | opCmp | - |
+= | não | opAddAssign | - |
-= | não | opSubAssign | - |
*= | não | opMulAssign | - |
/= | não | opDivAssign | - |
%= | não | opModAssign | - |
&= | não | opAndAssign | - |
|= | não | opOrAssign | - |
^= | não | opXorAssign | - |
<<= | não | opShlAssign | - |
>>= | não | opShrAssign | - |
>>>= | não | opUShrAssign | - |
~= | não | opCatAssign | - |
in | não | opIn | - |
Dado um operador binário sobrecarregável op e sua função membro correspondente opfunc e opfunc_r, e a sintaxe:
a op ba seguinte sequencia de regras é aplicada, na ordem, para determinar que forma é usada:
- A expressão é reescrita como ambas:
a.opfunc(b)
Se a.opfunc ou b.opfunc_r existir, então a sobrecarga é aplicada através de todas elas e a melhor combinação é usada. Se ambas existires, e não houver argumento que combine, então é um erro.
b.opfunc_r(a) - Se o operador é comutativo, então as
seguintes formas são tentadas:
a.opfunc_r(b)
b.opfunc(a) - Se a ou b for uma referência de objeto de classe ou estrutura, é um erro.
Exemplos
-
class A { int opAdd(int i); }
A a;
a + 1; // equivalente à a.opAdd(1)
1 + a; // equivalente à a.opAdd(1) -
class B { int opDiv_r(int i); }
B b;
1 / b; // equivalente à b.opDiv_r(1) -
class A { int opAdd(int i); }
class B { int opAdd_r(A a); }
A a;
B b;
a + 1; // equivalente à a.opAdd(1)
a + b; // equivalente à b.opAdd_r(a)
b + a; // equivalente à b.opAdd_r(a) -
class A { int opAdd(B b); int opAdd_r(B b); }
class B { }
A a;
B b;
a + b; // equivalente à a.opAdd(b)
b + a; // equivalente à a.opAdd_r(b) -
class A { int opAdd(B b); int opAdd_r(B b); }
class B { int opAdd_r(A a); }
A a;
B b;
a + b; // ambiguo: a.opAdd(b) ou b.opAdd_r(a)
b + a; // equivalente à a.opAdd_r(b)
Sobrecarregando == e !=
Ambos os operadores usam a função opEquals(). A expressão (a == b) é reescrita como a.opEquals(b), e (a != b) é reescrita como !a.opEquals(b).A função membro opEquals() é definida como parte de Object como:
int opEquals(Object o);então todo objeto de classe tem um opEquals().
Se uma estrutura não tem a função opEquals() declarada para ele, uma comparação dos conteúdos das duas estruturas é feita para determinar igualdade ou inegualdade.
Sobrecarregando <, <=, > e >=
Esses operadores de comparação todos usam a função opCmp(). A expressão (a op b) é reescrita como (a.opCmp(b) op 0). A operação comutativa é reescrita como (0 op b.opCmp(a))A função membro opCmp() é definida como parte de Object como:
int opCmp(Object o);então todo objeto de classe tem um opCmp().
Se uma estrutura não tem função opCmp() declarada para ela, tentar comparar duas estruturas é um erro.
Nota: Comparar uma referência para um objeto de classe com null deveria ser feito com:
if (a is null)e não com:
if (a == null)O último é convertido para:
if (a.opCmp(null))que falhará se opCmp() for uma função virtual.
Razão
A razão para ter opEquals() e opCmp() é que:- Testar por igualdade pode algumas vezes ser uma operação muito mais eficiênte que testar por maior ou menor que.
- Para alguns objetos, testar menor ou maior não
faz sentido. Para eles, anule opCmp() com:
class A
{
int opCmp(Object o)
{
assert(0); // comparação não faz sentido
return 0;
}
}
Sobrecarga do Operador de Chamada de Função f()
O operador de chamada de função, (), pode ser sobrecarregado declarando uma função chamada opCall:struct FDesse modo um objeto de classe ou estrutura pode se comportar como se fosse uma função.
{
int opCall();
int opCall(int x, int y, int z);
}
void test()
{ F f;
int i;
i = f(); // o mesmo que i = f.opCall();
i = f(3,4,5); // o mesmo que i = a.opCall(3,4,5);
}
Sobrecarda de Operadores de Arrays
Sobrecarregando Indexação a[i]
O operador deíndice do array, [], pode ser sobrecarregado declarando uma função chamada opIndex com um ou mais parâmetros. Atribuição para um array pode ser sobrecarregado com uma função chamada opIndexAssign com dois ou mais parâmetros. O primeiro parâmetro é o rvalue da expressão de atribuição.struct ADesse modo um objeto de classe ou estrutura pode se comportar como se fosse um array.
{
int opIndex(size_t i1, size_t i2, size_t i3);
int opIndexAssign(int value, size_t i1, size_t i2);
}
void test()
{ A a;
int i;
i = a[5,6,7]; // o mesmo que i = a.opIndex(5,6,7);
a[i,3] = 7; // o mesmo que a.opIndexAssign(7,i,3);
}
Nota: Sobrecarga de índuce de array atualmente não trabalha para o lvalue de um operador op=, ++, ou --.
Sobrecarregarga de Fatiar a[] e a[i .. j]
Sobrecarregar o operador de fatiar significa sobrecarregar expressões como a[] e a[i .. j]. Isso pode ser feito declarando uma função chamada opSlice. Atribuição à uma fatia pode ser feita declarando opSliceAssign.class A
{
int opSlice(); // sobrecarrega a[]
int opSlice(size_t x, size_t y); // sobrecarrega a[i .. j]
int opSliceAssign(int v); // sobrecarrega a[] = v
int opSliceAssign(int v, size_t x, size_t y); // sobrecarrega a[i .. j] = v
}
void test()
{ A a = new A();
int i;
int v;
i = a[]; // o mesmo que i = a.opSlice();
i = a[3..4]; // o mesmo que i = a.opSlice(3,4);
a[] = v; // o mesmo que a.opSliceAssign(v);
a[3..4] = v; // o mesmo que a.opSliceAssign(v,3,4);
}