Reputation: 543
I am working on an implementation of the complex numbers. The class, Complex
, has two private members, real_part
and imaginary_part
. I would like to override the multiplication operation as follows:
template<typename T, typename D>
friend Complex operator * (T lhs, D rhs)
{
double real_a;
double real_b;
double imaginary_a;
double imaginary_b;
if(std::is_same<T, Complex>::value)//if lhs is a Complex
{
real_a = lhs.real_part;
imaginary_a = lhs.imaginary_part;
}
else //base type, some sort of number
{
real_a = lhs;
imaginary_a = 0;
}
if(std::is_same<D, Complex>::value)//if rhs is a Complex
{
real_b = rhs.real_part;
imaginary_b = rhs.imaginary_part;
}
else //base type, some sort of number
{
real_b = rhs;
imaginary_b = 0;
}
Complex result;
result.real_part = (real_b*real_a- imaginary_b*imaginary_a);
result.imaginary_part = (real_b*imaginary_a + imaginary_b*real_a);
return result;
}
My constructors look like:
Complex::Complex()
{
real_part = 0.0;
imaginary_part = 0.0;
}
and:
Complex(T real, T imaginary)
{
real_part = real;
imaginary_part = imaginary;
}
When I try to multiply two Complex together:
Complex a(4.0, 8.0);
Complex b(8, 16);
auto prod = a*b;
auto prod2 = a * 2;
I receive the following error:
In file included from main.cpp:2:
complex.hpp: In instantiation of ‘Complex operator*(T, D) [with T = Complex; D = Complex]’:
main.cpp:13:17: required from here
complex.hpp:43:16: error: cannot convert ‘Complex’ to ‘double’ in assignment
real_a = lhs;
~~~~~~~^~~~~
complex.hpp:53:16: error: cannot convert ‘Complex’ to ‘double’ in assignment
real_b = rhs;
~~~~~~~^~~~~
complex.hpp: In instantiation of ‘Complex operator*(T, D) [with T = Complex; D = int]’:
main.cpp:14:20: required from here
complex.hpp:43:16: error: cannot convert ‘Complex’ to ‘double’ in assignment
real_a = lhs;
~~~~~~~^~~~~
complex.hpp:48:22: error: request for member ‘real_part’ in ‘rhs’, which is of non-class type ‘int’
real_b = rhs.real_part;
~~~~^~~~~~~~~
complex.hpp:49:27: error: request for member ‘imaginary_part’ in ‘rhs’, which is of non-class type ‘int’
imaginary_b = rhs.imaginary_part;
~~~~^~~~~~~~~~~~~~
I am trying to do the operator overload this way (with two generic types) to avoid having multiple overload multiplication operators (i.e. where LHS is a generic and RHS is of type Complex, vice-versa, etc). Would appreciate any help as I am not sure what I'm doing wrong.
Upvotes: 1
Views: 208
Reputation: 341
I agree with Michael Veksler's answer, i.e., the basic rule is that template functions will be compiled to multiple instances according to how you have invoked the function. The compilation will be done for all code while the assertion of std::is_same
is at runtime. Hence, the compilation will not pass as some branches of your code are invalid for specific argument types (e.g. int
for the real_b = rhs.real_part
statement).
To simplify the code, we can define another Constructor for the Complex
type by first casting simple numerical types (e.g., int
or double
) into Complex
type.
template<typename T>
Complex(T real, T imaginary = 0)
{
real_part = real;
imaginary_part = imaginary;
}
Then we can modify the *
operator overloading function as
static Complex operator * (const Complex & lhs, const Complex & rhs)
{
Complex result(lhs.real_part*rhs.real_part - lhs.imaginary_part * rhs.imaginary_part,
lhs.real_part*rhs.imaginary_part + lhs.imaginary_part * rhs.real_part);
return result;
}
NOTE that: the input arguments will be auto-cast into the Complex
type by the newly defined Constructor or the copy Constructor. Also, the returned result
is constructed with the new Constructor.
Upvotes: 1
Reputation: 66459
An instantiation of a template has to type-check in its entirety.
While you could use if constexpr
to select a branch at compile time, I think this is actually simpler the old-fashioned way, with overloading and no conditionals:
// Mutating multiplication as a member
template<typename T>
Complex<T>& Complex<T>::operator*=(const Complex<T>& rhs)
{
auto real = real_part;
auto imaginary = imaginary_part;
real_part = real * rhs.real_part - imaginary * rhs.imaginary_part;
imaginary_part = imaginary * rhs.real_part + real * rhs.imaginary_part;
return *this;
}
// These are free non-friend functions.
template<typename T>
Complex<T> operator*(Complex<T> lhs, const Complex<T>& rhs)
{
return lhs *= rhs;
}
template<typename T>
Complex<T> operator*(const Complex<T>& lhs, T rhs)
{
return lhs * Complex(rhs, 0);
}
template<typename T>
Complex<T> operator*(T lhs, const Complex<T>& rhs)
{
return rhs * lhs;
}
Upvotes: 1
Reputation: 8475
This is because both branches of the if
are compiled for the same instatiation. Some of these instantiations are invalid.
If you can work with c++17 then you can replace the if
with if constexr
, this way only the correct branch is instantiated:
if constexpr (std::is_same<T, Complex>::value)//if lhs is a Complex
{
real_a = lhs.real_part;
imaginary_a = lhs.imaginary_part;
}
else //base type, some sort of number
{
real_a = lhs;
imaginary_a = 0;
}
if constexpr (std::is_same<D, Complex>::value)//if rhs is a Complex
{
real_b = rhs.real_part;
imaginary_b = rhs.imaginary_part;
}
else //base type, some sort of number
{
real_b = rhs;
imaginary_b = 0;
}
For strict C++11, you can use function overloading to distinguish the two types:
template<typename T>
static T getReal(T x)
{
return x;
}
static double getReal(Complex x)
{
return x.real_part;
}
template<typename T>
static T getImaginary(T x)
{
return 0;
}
static double getImaginary(Complex x)
{
return x.imaginary_part;
}
template<typename T, typename D>
friend Complex operator * (T lhs, D rhs)
{
double real_a = getReal(lhs);
double real_b = getReal(rhs);
double imaginary_a = getImaginary(lhs);
double imaginary_b = getImaginary(rhs);
Complex result;
result.real_part = (real_b*real_a- imaginary_b*imaginary_a);
result.imaginary_part = (real_b*imaginary_a + imaginary_b*real_a);
return result;
}
Upvotes: 1