优化变量值的重复赋值
Optimizing repeating assignment of variable values
为了学习 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
,因为之前的操作修改了值。
有没有办法解决这个问题?或者是否有更有效的方法来实现相同的输出? (我会很感激一个代码示例)
你误解了复合操作。它们只影响表达式的左值。
#include <iostream>
int main() {
int b05 = 10; int b06 = 5;
b05 |= b06;
std::cout << "b05 " << b05 << ", b06 " << b06 << "\n";
}
输出:
b05 15, b06 5
b06 未修改。
你可以用宏做这样的事情:
#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 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,每次还是会发生拷贝:巧合的是,main
中的实参a
和b
被拷贝到函数的形参变量中也命名为 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 int b = 13;
然后不要传递 b
,只需使用 b
。
注意:请原谅我的错别字。我手边没有编译器,所以无法验证这段代码。它可能有一些小错误。如果你找到它们请留下评论,我会更新我的 post。
也许你可以做一个这样的函数:
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% 正确,但它应该能为您提供思路并且原理是合理的。当然,它不适用于原始类型。
为了学习 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
,因为之前的操作修改了值。
有没有办法解决这个问题?或者是否有更有效的方法来实现相同的输出? (我会很感激一个代码示例)
你误解了复合操作。它们只影响表达式的左值。
#include <iostream>
int main() {
int b05 = 10; int b06 = 5;
b05 |= b06;
std::cout << "b05 " << b05 << ", b06 " << b06 << "\n";
}
输出:
b05 15, b06 5
b06 未修改。
你可以用宏做这样的事情:
#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 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,每次还是会发生拷贝:巧合的是,main
中的实参a
和b
被拷贝到函数的形参变量中也命名为 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 int b = 13;
然后不要传递 b
,只需使用 b
。
注意:请原谅我的错别字。我手边没有编译器,所以无法验证这段代码。它可能有一些小错误。如果你找到它们请留下评论,我会更新我的 post。
也许你可以做一个这样的函数:
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% 正确,但它应该能为您提供思路并且原理是合理的。当然,它不适用于原始类型。