Элегантная перегрузка оператора в 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);
}
}
Это хорошо, только я бы предпочел, чтобы все было "дома". Есть ли способ сделать это без помощи шаблона? Я знаю, что я привередлив, потому что нет потери производительности и нетрудно импортировать модуль в ситуации, когда мне нужно это сделать. Мне просто интересно, встроен ли он, и я что-то пропускаю.
2 ответа
Почти все перегруженные операторы в D являются шаблонами по определению. Заметить, что void opOpAssign(string op)(Vector vector)
имеет параметр шаблона, который является строкой. Таким образом, нет, вы не можете перегрузить его как не шаблонную функцию. Теперь вам не нужен второй шаблон для этого (поэтому, если вы спрашиваете, нужен ли вам шаблон, вы имеете в виду вспомогательный шаблон, тогда ответ - нет), но перегруженная операторная функция уже является шаблоном.
Канонический способ сделать то, что вы пытаетесь сделать, это использовать строковые миксины:
void opOpAssign(string op)(Vector vector)
{
mixin("X" ~ op ~ "=vector.X;");
mixin("Y" ~ op ~ "=vector.Y;");
}
Это должно сочетаться с миксином
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
)