【问题标题】:Optimizing repeating assignment of variable values优化变量值的重复赋值
【发布时间】:2016-12-14 20:18:14
【问题描述】:

为了学习 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,因为前面的操作修改了值。

有没有办法解决这个问题?还是有更有效的方法来实现相同的输出? (我希望有一个代码示例)

【问题讨论】:

  • 你确实必须不断重新分配b06,它不会被复合操作修改。
  • 那段代码看起来很奇怪,很奇怪,很奇怪。

标签: c++ performance variables repeat compound-assignment


【解决方案1】:

你可以用宏做这样的事情:

#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 &lt;codefile&gt;,您会看到宏(以及任何包含)已展开。

【讨论】:

  • 请不要推荐宏解决方案。这些大多不适合编写 c++ 代码。
  • 首选内联函数而不是宏。函数有类型安全,宏没有。
  • 好答案。毫无疑问,有人会出现并发布 10 页代码以在没有宏的情况下实现相同的目标,并声称它“更好”,因为它没有使用宏
  • 啊,废话,我不是故意要发起一场大火。我通常不在 C++ 代码中使用宏,当然通常也不推荐这种方法,但它似乎是解决 OP 特定难题的最佳方法(鉴于他此时基本上正在编写 C 代码)。
  • 这段代码占用的空间少得多,运行得更快。非常感谢!我会接受它作为答案。 致那些反对宏的人:如果您提供更高效(就代码长度和执行时的性能而言)的代码示例,我可能会接受它作为答案。
【解决方案2】:

首先,将变量命名为b05b06 并没有什么特别的原因,所以让我们使用ab,这样看起来不那么难看。

基本上,您希望复制您的一对值(两个整数,4913)并将副本传递给将更改它们的代码。

最简单的解决方案是创建 2 个额外的变量,将值分配给它们:

int a0 = 49, b0 = 13;

然后每次你想使用它们时,你可以复制分配:

a = a0; b = b0;

这将避免重复常量(您只指定一次4913),但是您仍然必须重复复制变量的运算符。

任何进一步的改进都不会让您避免这种复制,它仍然必须在每次操作之前完成,但您可以通过一些技巧避免一些源代码重复:

(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中的实参ab被复制到函数的形参变量中,巧合的是a和@987654338 @。

(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

注意:请原谅我的错别字。我没有方便的编译器,所以我无法验证这段代码。它可能有一些小错误。找到了就留下cmets吧,我会更新帖子的。

【讨论】:

    【解决方案3】:

    您误解了复合运算。它们只影响表达式的左值。

    #include <iostream>
    
    int main() {
        int b05 = 10; int b06 = 5;
        b05 |= b06;
        std::cout << "b05 " << b05 << ", b06 " << b06 << "\n";
    }
    

    http://ideone.com/5nT7pR

    输出:

    b05 15, b06 5
    

    b06 未修改。

    【讨论】:

    • 是的,我只是在发布此消息后才意识到这一点。我会为以后的观众更新代码。
    【解决方案4】:

    也许你可以做一个这样的函数:

    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% 正确的,但它应该给你的想法和原则是合理的。当然,它不适用于原始类型。

    【讨论】:

      猜你喜欢
      • 2017-07-01
      • 2013-09-13
      • 2017-06-20
      • 1970-01-01
      • 2017-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多