2011-10-19 20 views
19

Por un tiempo estaba confundido acerca de la dirección de sobrecarga del operador de D's, pero ahora me doy cuenta de que es un sistema hermoso ... si solo funciona con tipos de núcleo (int, flotante, etc.). Considere el código de seguimiento:Sobrecarga elegante del operador en D

struct Vector { 
    float X, Y; 

    void opOpAssign(string op)(Vector vector) { 
     X.opOpAssign!op(vector.X); // ERROR: no property "opOpAssign" for float 
     Y.opOpAssign!op(vector.Y); // ERROR: ditto 
    } 
} 

Esto sería hermoso código si ha funcionado, ya que se sobrecarga todos + =, - =, * =, etc .. operadores en un método. Sin embargo, como puede ver, no funciona de la caja. He creado una solución utilizando plantillas (dios Me encanta D):

template Op(string op, T) { 
    void Assign(ref T a, T b) { 
     static if (op == "+") a += b; 
      else if (op == "-") a -= b; 
      else if (op == "*") a *= b; 
      else if (op == "/") a /= b; 
    } 
} 

struct Vector { 
    float X, Y; 

    void opOpAssign(string op)(Vector vector) { 
     Op!(op, typeof(X)).Assign(X, vector.X); 
     Op!(op, typeof(Y)).Assign(Y, vector.Y); 
    } 
} 

Esto está bien, sólo el Yo más prefieren mantener todo "en casa". ¿Hay alguna manera de hacer que esto funcione sin la ayuda de una plantilla? Sé que estoy siendo exigente aquí, ya que no hay pérdida de rendimiento y no es difícil importar un módulo en una situación en la que necesito hacerlo. Me pregunto si está integrado y estoy pasando por alto algo.

+0

Tenga en cuenta que 'if' estático no se continuó después de' 'if's después else'. Tienes que repetir 'static' nuevamente. – Bolpat

Respuesta

21

Casi todos los operadores sobrecargados en D son las plantillas por definición. Observe que void opOpAssign(string op)(Vector vector) tiene un parámetro de plantilla que es una cadena. Entonces, no, no puedes sobrecargarlo como una función que no es de plantilla. Ahora, no necesita una segunda plantilla para hacerlo (de modo que si pregunta si necesita una plantilla, quiere decir una plantilla de ayuda, entonces la respuesta es no), pero la función de operador sobrecargado ya es una plantilla.

La forma canónica para hacer lo que usted está tratando de hacer aquí es utilizar mixins de cadena:

void opOpAssign(string op)(Vector vector) 
{ 
    mixin("X" ~ op ~ "=vector.X;"); 
    mixin("Y" ~ op ~ "=vector.Y;"); 
} 
+0

¡Maldición, eso fue rápido! ¡Gracias! –

13

este está destinado a ser combinado con mixins

void opOpAssign(string op)(Vector vector) { 
    mixin("X"~op~"=vector.X;"); 
    mixin("Y"~op~"=vector.Y;"); 
} 

por no hablar de esto puede ser fácilmente acoplado a otras operaciones aritméticas

Vector opBinary(string op)(Vector l)if(op=="+"||op=="-"){//only addition and subtraction makes sense for 2D vectors 
    mixin("return Vector(x"~op~"l.x,y"~op~"l.y;"); 
} 

///take in anything as long as a corresponding binaryOp exists 
///this essentially rewrites all "vec op= variable;" to "vec = vec op variable;" 
void opOpAssign(string op,T)(T l){ 
    this = this.binaryOp!op(l); 
} 

e incluso a otros escalar el Vector

Vector opBinary(string op)(real l)if(op=="*"||op=="/"){ 
    mixin("return Vector(x"~op~"l,y"~op~"l;"); 
} 

Vector opBinaryRight(string op)(real l)if(op=="*"){// for 2 * vec 
    return this*l; 
} 

tenga en cuenta que el opBinary s definido restringe lo que se puede pasar a opOpAssign pero se puede ir en ambos sentidos (opBinary definir en términos de opOpAssign)

+0

Johnathan publicó primero y explicó más, así que marqué el suyo como la respuesta, pero también me gustaría agradecerle por responder tan rápido. –

+0

@FiL revisa mi edición para ver un poco más de bondad de sobrecarga –