Подтвердить что ты не робот

Элегантная перегрузка оператора в D

Некоторое время я смущался о направлении перегрузки оператора 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);
    }
}

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

4b9b3361

Ответ 1

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

Канонический способ сделать то, что вы пытаетесь сделать здесь, - использовать mixings mix:

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

Ответ 2

это означает, что он сочетается с mixins

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

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)