8
\$\begingroup\$

In C++ (and C++11), classes defining a + and += operators often define a - and -= operators that do nearly the same thing (except + is replaced with - in the function).

What is the best way to avoid duplicated code here (and still achieve good performance)? I am sure there is a good way to do it with functors:

Here's what I've tried:

#include <iostream>
#include <functional>

struct num {
  int val;

  const num & operator +=(const num & rhs) {
    return op_equals<std::plus<int> >(rhs);
  }
  const num & operator -=(const num & rhs) {
    return op_equals<std::minus<int> >(rhs);
  }

  template <typename op>
  const num & op_equals(const num & rhs) {
    op operation;
    val = operation(val, rhs.val);
    return *this;
  }
};

int main() {
  num x{1};
  num y{2};
  x += y;

  std::cout << x.val << std::endl;
  return 0;
}

Note that this is a simple example; in reality the code in the += and -= operators is more complex.

Also, I am not married to functors in the solution-- any suggestions or advice?

\$\endgroup\$

migrated from stackoverflow.com Jun 28 '12 at 2:12

This question came from our site for professional and enthusiast programmers.

  • 1
    \$\begingroup\$ Have you looked at Boost.Operators? \$\endgroup\$ – ildjarn Jun 27 '12 at 21:52
  • 2
    \$\begingroup\$ Is replacing duplicated code with more code in total a win? \$\endgroup\$ – Jon Jun 27 '12 at 21:52
  • 4
    \$\begingroup\$ A common pattern is to implement operator+ in terms of operator+=, which minimises repeated code. \$\endgroup\$ – Oli Charlesworth Jun 27 '12 at 21:53
  • 2
    \$\begingroup\$ @Oliver: So using Boost is bad practice? \$\endgroup\$ – Niklas B. Jun 27 '12 at 21:53
  • 6
    \$\begingroup\$ I think in this particular case, KISS is the best practice. \$\endgroup\$ – Stephen Chu Jun 27 '12 at 21:54
3
\$\begingroup\$

I actually quite like this pattern and use it relatively often. I'm not sure if there is something better than functors for this - if there is, I'm not really aware of it. That being said, with C++11, I'd write this in a slightly different way:

struct num {
  int val;

  const num& operator +=(const num & rhs) {
    return op_equals(rhs, std::plus<int>());
  }

  const num& operator -=(const num & rhs) {
    return op_equals(rhs, std::minus<int>());
  }

  template <typename op>
  const num & op_equals(const num & rhs, op&& o) {
    val = o(val, rhs.val);
    return *this;
  }
};

Note the rvalue reference (which is actually a "universal reference", so can bind to either an lvalue or rvalue reference), and the movement of op to a parameter - I don't really like having to specify it as a template parameter which then gets instantiated and called in the function. I think this way is slightly cleaner.

\$\endgroup\$
  • \$\begingroup\$ +1 Nice suggestions. I know to some people this feels like overkill, but to me it feels elegant (no duplicated code = no finding errors twice...) \$\endgroup\$ – user Feb 21 '13 at 16:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.