Search code examples
c++c++11sfinaenarrowing

How can I use SFINAE to prevent narrowing in a template function?


I am trying to implement basic arithmetic operations for a Vector class and would like to support mixing underlying types while preventing narrowing from occurring.

template <typename T1,typename T2>
Vector<T1> operator+( Vector<T1> lhs, const Vector<T2>& rhs, std::enable_if< ! is_narrowing_conversion<T2,T1>::value >::type* = nullptr )
{ return lhs += rhs; }

I would like to implement is_narrowing_conversion so that it will only allow the conversion if the types do not narrow. Here are some examples:

Vector<double> a = Vector<double>() + Vector<float>(); //OK
Vector<float> a = Vector<float> + Vector<double>; //Fails to find the operator+ function

In the end I would like to write a second template operator+ function that will handle the inverse case by returning a Vector.

I found this post with an incomplete example. But it is insufficient as he notes that it disallows uint8_t to uint64_t conversions.

I also discovered Daniel Krügler's paper on fixing is_constructible. Specifically in this paper he mentions using list-initialization which already has the narrowing semantics working but I'm not sure how to convert what he mentions into a proper trait that I can use for SFINAE template deduction.


Solution

  • What about using SFINAE through std::common_type ?

    The following is a simplified example (no Vector but simples values; no operator+() but a sum() function) but I hope you can understand what I mean

    #include <iostream>
    #include <type_traits>
    
    
    template <typename T1, typename T2>
    T1 sum (T1 t1,
            T2 const & t2,
            typename std::enable_if<std::is_same<
                  T1,
                  typename std::common_type<T1, T2>::type
               >::value>::type * = nullptr)
     { return t1 += t2; }
    
    int main()
     {
       float  a      = 1.1f;
       double b      = 2.2;
       long double c = 3.3;
    
       std::cout << sum(b, a) << std::endl;
       std::cout << sum(c, a) << std::endl;
       std::cout << sum(c, b) << std::endl;
       // std::cout << sum(a, b) << std::endl; compilation error
       // std::cout << sum(a, c) << std::endl; compilation error
       // std::cout << sum(b, c) << std::endl; compilation error
     }