www.digitalmars.com
Last update Fri Jun 2 17:53:49 2006

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

Operadores Unários Sobrecarregáveis
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 a
onde 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

  1. class A { int opNeg(); }
    A a;
    -a; // equialente à a.opNeg();
  2. 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

Operadores Binários Sobrecarregáveis
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 b
a seguinte sequencia de regras é aplicada, na ordem, para determinar que forma é usada:
  1. A expressão é reescrita como ambas:
    a.opfunc(b)
    b.opfunc_r(a)
    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.

  2. Se o operador é comutativo, então as seguintes formas são tentadas:
    a.opfunc_r(b)
    b.opfunc(a)
  3. Se a ou b for uma referência de objeto de classe ou estrutura, é um erro.

Exemplos

  1. class A { int opAdd(int i); }
    A a;
    a + 1; // equivalente à a.opAdd(1)
    1 + a; // equivalente à a.opAdd(1)
  2. class B { int opDiv_r(int i); }
    B b;
    1 / b; // equivalente à b.opDiv_r(1)
  3. 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)
  4. 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)
  5. 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:

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 F
{
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);
}
Desse modo um objeto de classe ou estrutura pode se comportar como se fosse uma função.

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 A
{
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);
}
Desse modo um objeto de classe ou estrutura pode se comportar como se fosse um array.

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);
}

Direções Futuras

Os operadores =, !, ., &&, ||, ?:, e alguns outros provavelmente nunca serão sobrecarregáveis.