Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

warning C4244: 'argument' : conversion from 'double' to 'const int', possible loss of data

Tags:

c++

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?

like image 282
user2736224 Avatar asked Aug 31 '13 21:08

user2736224


1 Answers

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.

like image 152
Nawaz Avatar answered Nov 15 '22 05:11

Nawaz