Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a good way to extend a class with additional operators?

I have been missing some important functionality from std::complex like the ability to add a std::complex<float> and a std::complex<double> or operations like 1+c, i.e. between int and std::complex. My idea was to derive a new class my::complex from std::complex which just inherits everything that is already implemented and adds other stuff.

In order to make the code compatible with functions using std::complex, I added automatic conversion to and from std::complex (and also automatic conversions between my::complex<float> and my::complex<double>).

Now something like

my::complex<float> c1,c2,c3;
c1 = c2 + c3;

works even though I did not implement operator+ myself, since c2 and c3 are cast to std::complex, added and the result is cast back to my::complex. I am assuming that the compiler can optimize any actual copying away.

However, the following does not work:

c1 = 2*(c2+c3)

since std::complex can not be multiplied by an integer (same problem if I multiply by a double and have complex<float>s).

I thought "ok, it seems that I have to add operator+ after all", but if I do that, I get use of overloaded operator '+' is ambiguous since the compiler can between std::complex and my::complex implicitly.

Is there a better way to achieve what I want or do I need to completely reimplement everything in std::complex and throw out the inheritance?

like image 944
Viktor Dick Avatar asked May 28 '16 07:05

Viktor Dick


2 Answers

Adding these overloads as free functions is probably about as good a way to do the job as any. I wouldn't, however, write a separate one hard-coded for each possible pair of operand types--that would get tedious very quickly.

Instead, you undoubtedly want to write a template function to handle any pair of operand types. Something on this general order:

template <class T, class U>
auto operator*(T const &a, U const &b) -> typename std::common_type<T, U>::type 
{
    // code to produce the equivalent of: return a * b;
}

This way if you multiply a complex<double> by an int and a complex<float> by a double (etc.) you don't have to duplicate the function for each possible combination (and if you try to combine two types that don't have a common type such as complex<double> and std::string, it simply won't compile).

In this case, filling in the body is fairly simple: we have inputs of two different types, but we know (or can deduce) a common type that's compatible with both. We want to cast each input to that common type, and do the operation on the results from casting:

template <class T, class U>
auto operator*(T const &a, U const &b) -> typename std::common_type<T, U>::type 
{
    using ret_type = typename std::common_type<T, U>::type;
    return ret_type(a) * ret_type(b);
}
like image 140
Jerry Coffin Avatar answered Nov 15 '22 12:11

Jerry Coffin


For adding complex numbers of different types and other operators I would use free functions

std::complex<double> operator? (std::complex<double> a,std::complex<float> b){
    std::complex<double> result;
    // implement ?
    return result;
}
std::complex<double> operator? (std::complex<float> a,std::complex<double> b){
    return b ? a;
}

where ? is a placehold for the operator you want to have. Note that this is a pre-C++11 solution, for a much nicer way see Jerry Coffins answer.

For adding a number to a complex, like in 1+c, I would not make the effort to write an operator, but simply use c+1 instead (actually I am not sure if this works for std::complex but I would be surprised if not).

like image 33
463035818_is_not_a_number Avatar answered Nov 15 '22 12:11

463035818_is_not_a_number