2013-03-07 12 views
10

Czy istnieje sposób na podwojenie precyzji zwracanej przez pomnożenie (aby uniknąć przepełnienia)?Zmodyfikuj szablon szablonu C++ na "długi T"?

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

Coś jak:

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

Więc czy 'int', 'długi' lub 'double' została podana, a 'long int', 'long long' lub „long double "zostanie zwrócony z mnożenia.

To jest ogólne pytanie. Pracuję nad nim, używając podwójnie wewnętrznego. Ale moje pytanie brzmi, czy istnieje jakiś mechanizm promujący typ do jego "długiego" wariantu w C++?

+0

To chyba lepiej po prostu użyć 'długie long' jako swojej' T' jeśli chcesz uniknąć przepełnienia. – Pubby

+4

'long' nie jest kwalifikatorem, który można zastosować do nazwy typu; 'long int' jest niepodzielną nazwą typu, która jest pisana z dwoma słowami kluczowymi' long' i 'int'. –

+0

Nie rozumiem, dlaczego chcesz używać "długiej litery T". T jest parametrem szablonu i może być dowolnym typem pierwotnym lub instancją obiektu, który chcesz być, jak długo * operator jest nadpisuje. W niektórych przypadkach możesz rzucić T na długo. – user1929959

Odpowiedz

13

Możliwym rozwiązaniem jest określenie własnego typu cechę:

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... 

to jak byś go używać w swojej klasie:

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

I tu jest mały test:

#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!"); 
} 
+3

W zależności od przewidywanego użycia, możesz pominąć 'type' z ogólnego szablonu lub w inny sposób upewnić się, że dostałeś błąd kompilacji, jeśli próbujesz tego z typem, który nie ma wersji 'long'er i dlatego i tak przepełni się. Ze względów bezpieczeństwa można zdefiniować specjalizacje za pomocą 'int8_t' ->' int16_t', 'int16_t' ->' int32_t' i 'int32_t' ->' int64_t', aby upewnić się, że naprawdę zwiększasz rozmiar na każdym etapie. Ofc, który jest nieco mniej przenośny, ponieważ są to typy opcjonalne, ale w systemach kod nie przesyła się do 'multiply' może przepełnić, czego najlepiej unikać. –

+0

@SteveJessop: Rzeczywiście, dobra obserwacja.To zależy głównie od tego, jak 'MyClass' ma być używany –

+1

@SteveJessop: Część' unsigned' jest objęta komentarzem "i tak dalej" ;-) –

2

@Andy ma właściwą odpowiedź, która działa całkiem dobrze. Ale dla tych, którzy chcą się błąd kompilacji, jeśli MojaKlasa jest tworzony z typem, dla którego nie ma wartości „długi”, ja łączyć go z użytkownika @ SteveJessop doskonałą komentarzu otrzymując następujące rozwiązanie:

// --- 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; }; 

Przykład użycia '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; } 
} 

Przykład użycia MyClass:

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

A co z samotnym 'długim int' pomiędzy tymi wszystkimi' intX_t's? Co jeśli 'std :: int32_t' jest' typedef'ed na 'long int'? –

+0

@ ChristianRau: dobry połów. To zostało i zostało usunięte. –

Powiązane problemy