C++ Template problem adding two data types

后端 未结 9 1315
慢半拍i
慢半拍i 2021-02-13 02:51

I have a template class with an overloaded + operator. This is working fine when I am adding two ints or two doubles. How do I get it to add and int and a double and return th

相关标签:
9条回答
  • 2021-02-13 03:43

    Here be dragons. You're getting into parts of c++ that will probably result in a lot of questions posted to StackOverflow :) Think long and hard about if you really want to do this.

    Start with the easy part, you want to allow operator+ to add types that are not always the same as T. Start with this:

    template <typename T2>
    TemplateTest<T> operator+(const TemplateTest<T2>& rhs) {
      return TemplateTest<T>(this->x + rhs.x);
    }
    

    Note that this is templated on T2 as well as T. When adding doubleTt1 + intTt2, T will be doubleTt1 and T2 will be intTt2.

    But here's the big problem with this whole approach.

    Now, when you add a double and an int, what do you expect? 4 + 2.3 = 6.3? or 4 + 2.3 = 6? Who would expect 6? Your users should, because you're casting the double back to an int, thus losing the fractional part. Sometimes. Depending on which operand is first. If the user wrote 2.3 + 4, they would get (as expected?) 6.3. Confusing libraries make for sad users. How best to deal with that? I dunno...

    0 讨论(0)
  • 2021-02-13 03:43

    Newer answer to an old question. For C++0x you can go with decltype as other answers have talked about. I would argue that common_type is more made for the situation than decltype.

    Here is an example of common_type used in a generic add:

    #include <iostream>
    #include <array>
    #include <type_traits>
    
    using namespace std;
    
    template <typename T, typename U, unsigned long N>
     array<typename common_type<T, U>::type, N> // <- Gets the arithmetic promotion
     add_arrays(array<T, N> u, array<U, N> v)
    {
        array<typename common_type<T, U>::type, N> result; // <- Used again here
        for (unsigned long i = 0; i != N; ++i)
        {
            result[i] = u[i] + v[i];
        }
        return result;
    }
    
    int main()
    {
        auto result = add_arrays( array<int,    4> {1, 2, 3, 4},
                                  array<double, 4> {1.0, 4.23, 8.99, 55.31} );
    
        for (size_t i = 0; i != 4; ++i)
        {
            cout << result[i] << endl;
        }
    
        return 0;
    }
    

    it basically returns the value that different arithmetic operations would promote to. One nice thing about it is that it can take any number of template args. Note: don't forget to add the ::type at the end of it. That is what gets the actual type result that you want.

    For those working pre-c++11 still, there is a boost version of common_type as well

    0 讨论(0)
  • 2021-02-13 03:47

    If this is mainly for basic types, you could help yourself with a metafunction until the new standard rolls in. Something along the lines of

    template<typename T1,
             typename T2,
             bool T1_is_int = std::numeric_limits<T1>::is_integer,
             bool T2_is_int = std::numeric_limits<T2>::is_integer,
             bool T1_is_wider_than_T2 = (sizeof(T1) > sizeof(T2)) > struct map_type;
    
    template<typename T1, typename T2, bool b> struct map_type<T1, T2, b, b, true > { typedef T1 type; };
    template<typename T1, typename T2, bool b> struct map_type<T1, T2, b, b, false> { typedef T2 type; };
    template<typename T1, typename T2, bool b> struct map_type<T1, T2, false, true , b> { typedef T1 type; };
    template<typename T1, typename T2, bool b> struct map_type<T1, T2, true , false, b> { typedef T2 type; };
    
    template<typename T, typename U>
    typename map_type<TemplateTestT<T>, TemplateTest<U> >::type
    operator+(TemplateTest<T> const &t, TemplateTest<U> const &u) {
      return typename map_type<TemplateTest<T>, TemplateTest<U> >::type(x + t1.x);
    }
    

    Of course, this is best combined with the char_traits idea:

    template <typename A, typename B>
    struct add_traits
    {
      typedef A first_summand_t;
      typedef B second_summand_t;
      typedef typename map_type<A, B>::type sum_t;
    };
    

    So that you can still specialise for types that don't have a numeric_limits overload.

    Oh, and in production code, you'll probably want to properly namespace that and add something for signed/unsigned mismatches in integer types.

    0 讨论(0)
提交回复
热议问题