8

I have to overload the basic arithmetic operators for some very complicated objects I've made. So far, I've successfully implemented operator*; now I need operator+, etc. The code for operator* is very large, but the only difference between operator* and operator+ will be one line where I use + instead of * on some complex numbers. This line will be inside of a loop that gets called many many times, so I want it to be efficient, which would seem to imply no function pointers. (Correct me if I'm wrong.)

This seems like a perfect use for templates. But I'm at a loss as to the correct syntax. I'm thinking something like this inside the ComplicatedObject class definition:

template <typename ComplexBinaryOp>
ComplicatedObject BinaryOp(const ComplicatedObject& B) const {
  // Do lots of stuff
  for(unsigned int i=0; i<OneBazillion; ++i) {
    // Here, the f[i] are std::complex<double>'s:
    C.f[i] = ComplexBinaryOp(f[i], B.f[i]);
  }
  // Do some more stuff
  return C;
}

inline ComplicatedObject operator*(const ComplicatedObject& B) const {
  return BinaryOp<std::complex::operator*>(B);
}

inline ComplicatedObject operator+(const ComplicatedObject& B) const {
  return BinaryOp<std::complex::operator+>(B);
}

This question is related: "function passed as template argument". But the functions passed as the template arguments are not operators.

I've fiddled with the syntax every way I can think of, but the compiler always complains of bad syntax. How should I do this?

Edit:

For clarity, I include the complete solution in terms of my code above, along with the additional generalizations people may need:

template <typename ComplexBinaryOp>
ComplicatedObject BinaryOp(const ComplicatedObject& B) const {
  // Do lots of stuff
  for(unsigned int i=0; i<OneBazillion; ++i) {
    // Here, the f[i] are std::complex<double>'s:
    C.f[i] = ComplexBinaryOp()(f[i], B.f[i]); // Note extra ()'s
  }
  // Do some more stuff
  return C;
}

inline ComplicatedObject operator+(const ComplicatedObject& B) const {
  return BinaryOp<std::plus<std::complex<double> > >(B);
}

inline ComplicatedObject operator-(const ComplicatedObject& B) const {
  return BinaryOp<std::minus<std::complex<double> > >(B);
}

inline ComplicatedObject operator*(const ComplicatedObject& B) const {
  return BinaryOp<std::multiplies<std::complex<double> > >(B);
}

inline ComplicatedObject operator/(const ComplicatedObject& B) const {
  return BinaryOp<std::divides<std::complex<double> > >(B);
}
Community
  • 1
  • 1
Mike
  • 19,114
  • 12
  • 59
  • 91
  • 1
    `std::complex` is a class template, so you need `std::complex`. But even then, `complex::operator*` and `complex::operator+` are *member functions*. You can't just pass them around without an instance of `complex` to operate on. – Praetorian Jun 26 '13 at 04:02
  • Post errors you are getting. – n. m. could be an AI Jun 26 '13 at 04:02
  • 1
    +1 for interesting question. I also found this question -- [c++ pointers to operators](http://stackoverflow.com/questions/4176895/c-pointers-to-operators) relevant and interesting. – keelar Jun 26 '13 at 04:06

2 Answers2

4

I think std::plus<std::complex> and std::multiplies<std::complex> are what you're looking for, but I'm not 100% sure I understand your question (is your code snippet within a class you aren't showing us?)

Mark B
  • 95,107
  • 10
  • 109
  • 188
  • +1, I think this is what the OP is looking for too, but it should be `plus>` and `multiplies>` – Praetorian Jun 26 '13 at 04:04
  • @Praetorian: so `plus` and `multiplies` can be used without specifying the `std` scope? – keelar Jun 26 '13 at 04:08
  • +1 Yes, this looks like what I want, alright (though @Praetorian is right). But I can't quite figure out how to use these functions. The compiler objects to my line `ComplexBinaryOp(f[i], B.f[i])`. Any help on that? – Mike Jun 26 '13 at 04:12
  • @keelar That's not what I meant. `std::complex` is a class template, so it needs to be specialized for a type. `std::plus>` and so forth. (Although you *could* use `plus` and `multiplies` unqualified as long as you qualify `complex` because of [ADL](https://en.wikipedia.org/wiki/Argument-dependent_name_lookup)) – Praetorian Jun 26 '13 at 04:14
  • @Praetorian: sorry for my pickyness. Your point is clear. I was thinking about using the same notation as the original answer might be better. – keelar Jun 26 '13 at 04:36
  • 2
    @me `ComplexBinaryOp()(f[i], B.f[i])` does the job. Note the extra parentheses. – Mike Jun 26 '13 at 04:37
1

You have two options. Pass the function at runtime:

#include <functional>

template <typename ComplexBinaryOp>
ComplicatedObject BinaryOp(const ComplicatedObject& B, ComplexBinaryOp op) const {
  // ...
    C.f[i] = op(f[i], B.f[i]);
  // ...
}

// functor wrapping member function pointer
BinaryOp(B, std::mem_fn(&std::complex<double>::operator+));

// standard-issue functor
BinaryOp(B, std::plus<std::complex<double>>());

Or pass it at compile-time:

// or another floating-point type
typedef double (*ComplexBinaryOp)(double, double);

template <ComplexBinaryOp op>
ComplicatedObject BinaryOp(const ComplicatedObject& B) const {
  // ...
    C.f[i] = op(f[i], B.f[i]);
  // ...
}

// non-member function
template<class T>
std::complex<T> add_complex(const std::complex<T>& a, const std::complex<T>& b) {
  return a + b;
}

// non-member function pointer
BinaryOp<add_complex<double>>(B);

I believe you can do the same with member function pointers as well by changing the definition of ComplexBinaryOp.

Jon Purdy
  • 53,300
  • 8
  • 96
  • 166
  • These look like useful possibilities. I'll try things out and get back to you. Thanks! – Mike Jun 26 '13 at 04:14
  • Turns out my original syntax works just fine, after replacing `std::complex::operator*` with `std::multiplies`, etc. Yours also work, but they strike me as less simple. – Mike Jun 26 '13 at 04:42
  • 1
    @Mike: Yeah, that works too. You do have to say `ComplexBinaryOp()(foo, bar)` to actually get the functor instance, though. That can just as well be done inside or outside as in my first example. – Jon Purdy Jun 26 '13 at 23:08