Изменить тип шаблона С++ T как "long T"?

Есть ли способ удвоить точность, возвращаемую умножением (чтобы избежать переполнения)?

template<class T> class MyClass {
     T multiply (T a, T b) { return a * b; }
}

Что-то вроде:

long T multiply (T a, T b) { return a * b; }

Таким образом, если бы были заданы "int", "long" или "double", то из long-long будет длинный int, long long или long long.

Это общий вопрос. Я работаю над этим, используя двойное внутреннее. Но мой вопрос заключается в том, есть ли какой-либо механизм продвижения типа к его "длинному" варианту в С++?

Ответ 1

Возможное решение - определить свой собственный характер:

template<typename T>
struct add_long { typedef T type; };

template<>
struct add_long<int> { typedef long int type; };

template<>
struct add_long<double> { typedef long double type; };

template<>
struct add_long<long int> { typedef long long int type; };

// And so on...

Вот как вы могли бы использовать его в своем классе:

template<class T>
class MyClass {
public:
    typedef typename add_long<T>::type longT;
    longT multiply (longT a, longT b) { return a * b; }
};

И вот небольшой тест:

#include <type_traits>

int main()
{
    MyClass<int> m;
    auto l = m.multiply(2, 3);
    static_assert(std::is_same<decltype(l), long int>::value, "Error!");
}

Ответ 2

@У Энди есть правильный ответ, который работает достаточно хорошо. Но для тех, кто хочет ошибку времени компиляции, если MyClass создается с типом, для которого нет "длинного" значения, я объединил его с замечательным комментарием @SteveJessop, чтобы дать следующее решение:

// --- Machinery to support double-precision 'T' to avoid overflow in method 'multiply' ---
// Note: uncomment typedef if don't want compile-time errors 
// when no "long" type exists
// ----
template<typename T>
struct add_long { /*typedef T type;*/ };

template<> struct add_long<int8_t>   { typedef int16_t  type; };
template<> struct add_long<int16_t>  { typedef int32_t  type; };
template<> struct add_long<int32_t>  { typedef int64_t  type; };
template<> struct add_long<uint8_t>  { typedef uint16_t type; };
template<> struct add_long<uint16_t> { typedef uint32_t type; };
template<> struct add_long<uint32_t> { typedef uint64_t type; };

template<> struct add_long<float>    { typedef double        type; };
template<> struct add_long<double>   { typedef long double   type; };

Пример использования longT:

template<class T> class MyClass
{
    // Note: a compiler error on the next line means that 
    //       class T has no double-precision type defined above.
    typedef typename add_long<T>::type longT;
public:
    longT multiply (T a, T b) { return longT(a) * b; }
}

Пример использования MyClass:

MyClass<float> my;
printf("result = %lf\n", my.multiply(3.4e38, 3.4e38));