Стремясь изучить составное присваивание в C ++, я создал следующий код, чтобы продемонстрировать, что они делают:
int b05;
int b06 = 13;
b05 = 49;
b05 += b06; // b05 = b05 + b06
cout << "(+=) compound assignment: " << b05 << endl;
b05 = 49;
b05 -= b06; // b05 = b05 - b06
cout << "(-=) compound assignment: " << b05 << endl;
b05 = 49;
b05 *= b06; // b05 = b05 * b06
cout << "(*=) compound assignment: " << b05 << endl;
b05 = 49;
b05 /= b06; // b05 = b05 / b06
cout << "(/=) compound assignment: " << b05 << endl;
b05 = 49;
b05 %= b06; // b05 = b05 % b06
cout << "(%=) compound assignment: " << b05 << endl;
b05 = 49;
b05 >>= b06; // b05 = b05 >> b06
cout << "(>>=) compound assignment: " << b05 << endl;
b05 = 49;
b05 <<= b06; // b05 = b05 << b06
cout << "(<<=) compound assignment: " << b05 << endl;
b05 = 49;
b05 &= b06; // b05 = b05 & b06
cout << "(&=) compound assignment: " << b05 << endl;
b05 = 49;
b05 ^= b06; // b05 = b05 ^ b06
cout << "(^=) compound assignment: " << b05 << endl;
b05 = 49;
b05 |= b06; // b05 = b05 | b06
cout << "(|=) compound assignment: " << b05 << endl;
Как видите, мне нужно переназначить значение 49 на b05
потому что предыдущая операция изменяет значения.
Есть ли способ обойти это? Или есть более эффективный метод для достижения того же результата? (Я был бы признателен за пример кода)
Вы можете сделать что-то вроде этого с помощью макроса:
#define compound(op) \
b05 = 49; \
b05 op b06; \
std::cout << "(" << #op << ") compound assignment: " << b05 << std::endl;
Тогда вы называете это так:
int b05, b06 = 13;
compound(+=)
compound(/=)
// etc...
По сути, это замена текста во время компиляции. В каждом месте у вас есть compound(...)
будет заменен текстом составного макроса с op
заменяется тем, что вы указали в скобках (в данном случае оператором какого-то рода).
Чтобы увидеть это в действии, сделайте g++ -E <codefile>
и вы увидите, что макрос (и любые включения) был расширен.
Прежде всего, нет особой причины для именования ваших переменных b05
а также b06
так что давайте использовать a
а также b
выглядит менее уродливо.
По сути, вы хотите сделать копию вашей пары значений (два целых, 49
а также 13
) и передайте копию коду, который изменит их.
Самое простое решение — вы создаете 2 дополнительные переменные и присваиваете им значения:
int a0 = 49, b0 = 13;
и затем каждый раз, когда вы хотите использовать их, вы можете скопировать назначение:
a = a0; b = b0;
Это позволит избежать дублирования констант (вы указываете только 49
а также 13
один раз), однако вам все равно придется дублировать операторы, которые копируют переменные.
Любые дальнейшие улучшения не позволят вам избежать этой копии, это все еще нужно делать перед каждой операцией, но вы можете избежать некоторого дублирования исходного кода с помощью нескольких хитростей:
(1) Использование struct. Это позволит вам инкапсулировать два значения в одно:
struct params {
int a;
int b;
}
params p0;
p0.a = 49;
p0.b = 13;
params p;
...
p = p0;
p.a += p.b; // p.a = p.a + p.b
cout << "(+=) compound assignment: " << p.b << endl;
...
Теперь вы повторяете только одну команду p = p0
вместо двух.
(2) Использование параметров значения:
void test1(int a, int b) {
a += b; cout << "(+=) compound assignment: " << a << endl;
}
void test2(int a, int b) {
a -= b; cout << "(-=) compound assignment: " << a << endl;
}
...
int main() {
int a = 49; int b = 13;
test1(a, b);
test2(a, b);
...
return 0;
}
Хотя явно не назначаются a и b, копия все равно происходит каждый раз: фактические параметры a
а также b
от main
копируются в переменные формальных параметров функций, по совпадению также называемые a
а также b
,
(3) Вы можете избежать дублирования кода, используя ссылки на функции. Вот пример с массивом анонимных функций для каждой операции:
typedef int OperationFn(int, int)
struct OperationInfo {
std::string label;
OperationFn op;
}
OperationInfo operations[9] = {
{ "+=", [](int a, int b) { return a += b } },
{ "-=", [](int a, int b) { return a -= b } },
{ "*=", [](int a, int b) { return a *= b } },
{ "/=", [](int a, int b) { return a /= b } },
{ "%=", [](int a, int b) { return a %= b } },
{ ">>=", [](int a, int b) { return a >>= b } },
{ "<<=", [](int a, int b) { return a <<= b } },
{ "&=", [](int a, int b) { return a &= b } },
{ "^=", [](int a, int b) { return a ^= b } },
}
int main() {
for (int i = 0; i < 9; i++) {
const OperationInfo& oi = operations[i];
cout << "(" << oi.label << ") compound assignment: ";
cout << oi.op(49, 13) << endl;
}
return 0;
}
В коде упоминается подстановка параметров только один раз (oi.op(49, 13)
линии), но он находится в цикле, поэтому он будет эффективно выполняться все 9 раз. Хотя код выглядит чище и компактнее, выигрыша в эффективности нет: вы все равно копируете оба значения перед каждым тестом.
Однако возможна одна оптимизация: вы можете использовать общее значение const для второго аргумента, потому что он никогда не изменяется:
const int b = 13;
а потом не проходи b
вокруг, просто используйте b
,
Примечание: простите мои опечатки. У меня нет удобного компилятора, поэтому я не могу проверить этот код. Это может иметь незначительные ошибки. Оставляйте комментарии, если найдете, я обновлю свой пост.
Вы неправильно поняли сложные операции. Они влияют только на левое значение выражения.
#include <iostream>
int main() {
int b05 = 10; int b06 = 5;
b05 |= b06;
std::cout << "b05 " << b05 << ", b06 " << b06 << "\n";
}
Выходы:
b05 15, b06 5
B06 был неизменен.
Может быть, вы могли бы сделать такую функцию:
class Obj
{
public:
Obj& operator+= (const Obj &) { return *this; }
Obj& operator-= (const Obj &) { return *this; }
};
void testFunction(std::function<Obj&(Obj *, const Obj &)> functionToTest)
{
Object object;
Object otherObject;
functionToTest(&object, otherObject);
cout << "(+=) function test: " << object << endl;
}
int main(void)
{
std::function<Obj&(Obj *, const Obj &)> functionToTest(&Obj::operator+=);
testFunction(functionToTest);
std::function<Obj&(Obj *, const Obj &)> functionToTest2(&Obj::operator-=);
testFunction(functionToTest2);
}
Этот код не на 100% правильный, но он должен дать вам идею & принцип звука. Конечно, это не работает с примитивными типами.