Элегантная перегрузка оператора в D - PullRequest
19 голосов
/ 19 октября 2011

Некоторое время я был озадачен направлением перегрузки операторов D, но теперь я понимаю, что это прекрасная система ... если бы она работала только с основными типами (int, float и т. Д.). Рассмотрим следующий код:

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

Это был бы красивый код, если бы он работал, поскольку он перегружает все операторы + =, - =, * = и т. Д. В одном методе. Однако, как вы можете видеть, это не работает из коробки. Я создал решение, используя шаблоны (боже, я люблю 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);
    }
}

Это хорошо, только я бы предпочел, чтобы все было "в доме". Есть ли способ сделать эту работу без помощи шаблона? Я знаю, что я привередлив, потому что нет потери производительности и нетрудно импортировать модуль в ситуации, когда мне нужно это сделать. Мне просто интересно, встроен ли он, и я что-то пропускаю.

Ответы [ 2 ]

22 голосов
/ 19 октября 2011

Почти все перегруженные операторы в D являются шаблонами по определению .Обратите внимание, что void opOpAssign(string op)(Vector vector) имеет параметр шаблона, который является строкой.Таким образом, нет, вы не можете перегрузить его как не шаблонную функцию.Теперь вам не нужен второй шаблон для этого (поэтому, если вы спрашиваете, нужен ли вам шаблон, вы имеете в виду вспомогательный шаблон, тогда ответ - нет), но перегруженная операторная функция уже является шаблоном.

Канонический способ сделать то, что вы пытаетесь сделать, это использовать строковые миксины:

void opOpAssign(string op)(Vector vector)
{
    mixin("X" ~ op ~ "=vector.X;");
    mixin("Y" ~ op ~ "=vector.Y;");
}
13 голосов
/ 19 октября 2011

это должно сочетаться с миксином

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

не говоря уже о том, что это можно легко связать с другими арифметическими операциями

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

и даже другому масштабирующему Вектор

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

обратите внимание, что определенные opBinary ограничивают то, что может быть передано opOpAssign, но вы можете пойти обоими путями (определите opBinary в терминах opOpAssign)

...