Я строю класс с немного асимметричным дополнением. Прежде чем поступить жалобы, она обязательно должна быть асимметричной. Существует преобразование (операция, которая занимает немного времени), которое должно произойти, когда два объекта добавляются вместе, и преобразование наиболее естественно происходит относительно правильного слагаемого.
Чтобы сделать это конкретным, вот общий пример того, что происходит …
class Foo {
char _fav;
int _prop;
public:
const char fav() const {return _fav;}
const int prop() const (return _prop;}
void changeFav(char); // complicated method that also changes _prop
void changeProp(int); // straightforward method
}
Foo
operator + (Foo A, Foo B) {
Foo sum;
if (A.fav() != B.fav()) A.changeFav(B.fav);
sum.changeProp(A.prop() + B.prop());
return sum;
}
На двоих Foo
s, которые будут добавлены, они должны иметь одинаковые _fav
Таким образом, выбор должен быть сделан, чтобы преобразовать. На основании деталей Foo
, наиболее естественно изменить левое слагаемое, чтобы оно соответствовало правому слагаемому.
Однако при выполнении:
Foo A,B,C;
Foo D = A + B + C; // D = (A + B) + C
Foo E = A + (B + C);
если A
уже имеет то же самое _fav
как C
, затем changeFav
вызывается дважды для D
(один раз поменять A._fav
в B._fav
а потом снова поменять (A+B)._fav
в C._fav
) и один раз для E
(изменить B._fav
в C._fav
). Я предпочитаю последнее, но хочу избегать принуждения пользователя использовать скобки для многократного добавления.
Есть ли способ перегрузить ассоциативность operator +
чтобы это произошло?
Из c ++ стандартного пункта 5,
Перегруженные операторы подчиняются правилам синтаксиса, указанным в разделе 5.
Где пункт 5 определяет приоритет оператора и ассоциативность.
Вы можете сделать взломать. Вы должны использовать другой тип для хранения промежуточных результатов операции, затем вы можете использовать неявное приведение для оценки результата. Вот пример того, как реализовать операторы сравнения в стиле Python в C ++:
#include <vector>
#include <cstdio>
struct S {
S(int x) : val(x) { }
int val;
};
struct Comparison {
std::vector<S> operands;
explicit Comparison(S x)
{
operands.push_back(x);
}
operator S()
{
auto i = operands.begin(), e = operands.end();
S prev = *i;
for (i++; i != e; i++) {
S cur = *i;
if (prev.val >= cur.val)
return S(0);
prev = cur;
}
return S(1);
}
void append(const Comparison &a)
{
operands.insert(
operands.end(),
a.operands.begin(),
a.operands.end());
}
void append(const S &a)
{
operands.push_back(a);
}
};
Comparison operator<(const Comparison &left, const Comparison &right)
{ Comparison result(left); result.append(right); return result; }
Comparison operator<(const Comparison &left, const S &right)
{ Comparison result(left); result.append(right); return result; }
Comparison operator<(const S &left, const Comparison &right)
{ Comparison result(left); result.append(right); return result; }
Comparison operator<(const S &left, const S &right)
{ Comparison result(left); result.append(right); return result; }
int main()
{
S x(0);
x = S(0) < S(1) < S(2) < S(3);
std::printf("0 < 1 < 2 < 3 = %d\n", x.val);
x = S(0) < S(1) < S(3) < S(2);
std::printf("0 < 1 < 3 < 2 = %d\n", x.val);
return 0;
}
Однако в такой ситуации я бы быстро бросил +
оператор. Я бы не использовал +
для любой операции, которая не является ассоциативной и коммутативной, так как это соглашение для +
по математике Вместо этого вы можете использовать переменную функцию (используя шаблоны) для выполнения желаемого вычисления.
Конечно, но вам не понравится «как».
Прежде всего, вам нужно прочитать и понять Boost.Proto документация. Затем вам нужно выяснить, как преобразовать все деревья выражений, чтобы изменить порядок операций. Затем необходимо сделать оценку деревьев выражений прозрачной для конечного пользователя. Возможно по заданию? Я не слишком много возился с Прото, но что-то похожее на эта статья о Прото-основанных оптимизациях может быть полезным для начала.
Нет. Так почему бы не изменить вместо этого предпочтение правого операнда?