I'm defining the '*' operator to work with a 'NumericArray' class template. The code is as following:
template <class T>
NumericArray<T> NumericArray<T>::operator * (const T& factor) const
{
NumericArray<T> newArray(Size());
for (int i = 0; i < Size(); i++)
newArray[i] = factor * GetE(i);
return newArray;
}
when I try to use a 'NumericArray' of type 'int' (NumericArray) with the '*' operator when the 'factor' argument is a double:
intArray1 = intArray1*2.5;
I get the following compiler warning:
warning C4244: 'argument' : conversion from 'double' to 'const int', possible loss of data
and the 'factor' arg is truncated from 2.5 to 2 (an int) before it is multiplied against elements of the 'NumericArray' object. Is there a way to prevent this from happening? I thought in C++ it was a simple matter of default protocol where int*double = double. Am I wrong? If not, why doesn't that apply in this case?
intArray1 = intArray1*2.5;
I guess intArray1
is of type NumericArray<int>
. If that is so, then T
is int
. So in the above expression, 2.5
which is a double
is going to be converted into int
, as it is passed to the overloaded operator*(const int&)
as argument.
That also means, 2.5 (double) becomes 2 (int) and factor
is basically 2
. Loss of data!
One way to fix this is to use function template (as member of the class template) as:
template<class T> //this is for class templatwe
template<class U> //this is for function template
NumericArray<T> NumericArray<T>::operator * (const U& factor) const
{ //^^^^^^^ it is U now!
//your code
}
Don't be surprised by two usage of template
in the above definition. The comments say what they're for. If you understand that well, then you also understand that the parameter now is U
instead of T
, it can be independent of T
and hence it can be whatever you pass to it as argument. No loss of data, as far as when passing the argument.
Now as you know the product of int
and double
turns out to be double
, then why return NumericArray<int>
from the function in case when you pass double
to it? I think it makes more sense to return NumericArray<double>
if the argument is double
. So the following seems to be correct implementation:
template<class T> //this is for class templatwe
template<class U> //this is for function template
NumericArray<U> NumericArray<T>::operator * (const U& factor) const
{ //^^^ CHANGED
NumericArray<U> newArray(Size()); //CHANGED HERE TOO!
for (int i = 0; i < Size(); i++)
newArray[i] = factor * GetE(i);
return newArray;
}
Wait! Is this correct now? What if T
is double
and U
is int
? The above has exactly the same problem as the previous one!
So here is third attempt:
template<class T> //this is for class templatwe
template<class U> //this is for function template
NumericArray<decltype(std::declval<T>() * std::declval<U>())>
NumericArray<T>::operator * (const U& factor) const
{
typedef decltype(std::declval<T>() * std::declval<U>()) R; //define R
NumericArray<R> newArray(Size()); //CHANGED HERE!
for (int i = 0; i < Size(); i++)
newArray[i] = factor * GetE(i);
return newArray;
}
So the return type is :
NumericArray<R>
where R
is:
decltype(std::declval<T>() * std::declval<U>());
which depends on the type of the product of T
and U
. That is correct now, at least much better.
Hope that helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With