2015-11-10 9 views
5

E 'una domanda da C++ Primer capitolo 16.2.3 (domanda 16.41):funzione Somma con tipo di ritorno abbastanza grande da contenere provocare

scrivere una versione di somma con un tipo di ritorno che è garantito per essere grande abbastanza per contenere il risultato dell'aggiunta.

Sono sicuro che ci potrebbe essere qualche funzione STL piuttosto oscuro che possono ottenere questo lavoro fatto, ma nel contesto del capitolo introduce modelli tipo di trasformazione standard come remove_reference<T> e make_signed<T> che sono sicuro che intende per me da usare per realizzare questo, in concomitanza con i tipi di ritorno finali. Il meglio che posso fare è:

template <typename It> auto sum(It first, It second) -> typename make_unsigned<It>::type { 
    return first+second; 
} 

Questo risponde alla domanda quasi ma non del tutto, ma non tiene conto di questo fatto che ho potuto passare due unsigned int s che aggiungono ad andare al di fuori della gamma di valori che un unsigned int può contenere (e quindi torna indietro a zero). Per quanto posso dire che i modelli di trasformazione non possono aiutare con quel problema, è in qualche modo possibile dedurre il tipo di ritorno come il prossimo tipo integrale più grande dal tipo integrale dedotto dagli argomenti passati?

+1

'unsigned long long int' –

+0

@willywonka_dailyblah Certamente vero ma che sembra molto forzato. La domanda potrebbe volere che la funzione sia usata anche con i tipi di classe. – AntiElephant

+0

prova 'sizeof'? e se è troppo breve, lancia un'eccezione. o fai un'asserzione statica: http://stackoverflow.com/questions/174356/ways-to-assert-expressions-at-build-time-in-c/174441#174441 –

risposta

3

Poiché si desidera eseguire questa operazione in fase di compilazione, non è possibile conoscere il valore degli argomenti con cui verrà invocata la funzione. Così si dovrebbe proteggere i agains straripante in fase di compilazione, e la cosa più ovvia che mi viene in mente è utilizzando una classe di promozione tratto:

#include <iostream> 
#include <limits> 

template<typename T> 
struct promote; 

template<> // and so on for all types that you want to promote 
struct promote<unsigned int> // type to be promoted from 
{ 
    using type = unsigned long int; // type to be promoted to 
}; 

// helper a la C++14 
template<typename T> 
using promote_t = typename promote<T>::type; 

template <typename It> 
auto my_sum(It first, It second) -> promote_t<It> 
{ 
    return static_cast<promote_t<It>>(first) + second; // promotion 
} 

int main() 
{ 
    unsigned int a = std::numeric_limits<unsigned int>::max(); 
    unsigned int b = std::numeric_limits<unsigned int>::max(); 

    auto c = my_sum(a, b); // type is promoted to unsigned long int 
    std::cout << "a = " << a << std::endl; 
    std::cout << "b = " << b << std::endl; 
    std::cout << "a + b = " << c << std::endl; 
} 

Live on Coliru

+0

Molto bello - se non ti dispiace, potresti spiegare perché è necessario "l'aiutante"? Non potresti semplicemente usare 'promuovere :: type' direttamente? –

+0

@PaulR Sì, è possibile. L'helper è fondamentalmente solo per "aiutarti" a digitare meno. Altrimenti devi continuare a digitare 'typename promuovi :: type' tutto il tempo (ricorda il' typename' dato che hai un tipo dipendente). E il 'static_cast >' diventerà miseramente lungo: 'static_cast :: type>'. Diventa davvero utile quando riutilizzi in altri scenari. – vsoftco

+0

Anche se questo dovrebbe essere scritto per piattaforma, nel caso in cui 'sizeof (unsigned int) == sizeof (unsigned long int)'. –

1

con un tipo di ritorno che è garantita essere abbastanza grande da contenere il risultato dell'aggiunta.

L'opzione facile è utilizzare un pacchetto matematico di precisione arbitrario.

L'opzione un po 'più difficile è scrivere un'aggiunta aritmetica di precisione arbitraria.

Ho un codice che utilizza un pacchetto, ma al momento non riesco a trovarlo. Aggiornerà quando lo trovo. Facile da usare.

Aggiornamento: la trovano

pacchetto chiamato gmpxx.h. (Penso spinta ha anche una classe adatta o 2)


Con uint64_t, fattoriale 93 (o forse 94?) Provoca avvolgono.

93! 
    1,156,772,507,081,641,574,759,205,162,306,240,436,214,753,229,576,413,535,186,142,281,213,246,807, 
121,467,315,215,203,289,516,844,845,303,838,996,289,387,078,090,752,000,000,000,000,000,000,000 

Con mpz_class,

1000! 
402,387,260,077,093,773,543,702,433,923,003,985,719,374,864,210,714,632,543,799,910,429,938,512,398, 
629,020,592,044,208,486,969,404,800,479,988,610,197,196,058,631,666,872,994,808,558,901,323,829,669, 
944,590,997,424,504,087,073,759,918,823,627,727,188,732,519,779,505,950,995,276,120,874,975,462,497, 
043,601,418,278,094,646,496,291,056,393,887,437,886,487,337,119,181,045,825,783,647,849,977,012,476, 
632,889,835,955,735,432,513,185,323,958,463,075,557,409,114,262,417,474,349,347,553,428,646,576,611, 
667,797,396,668,820,291,207,379,143,853,719,588,249,808,126,867,838,374,559,731,746,136,085,379,534, 
524,221,586,593,201,928,090,878,297,308,431,392,844,403,281,231,558,611,036,976,801,357,304,216,168, 
747,609,675,871,348,312,025,478,589,320,767,169,132,448,426,236,131,412,508,780,208,000,261,683,151, 
027,341,827,977,704,784,635,868,170,164,365,024,153,691,398,281,264,810,213,092,761,244,896,359,928, 
705,114,964,975,419,909,342,221,566,832,572,080,821,333,186,116,811,553,615,836,546,984,046,708,975, 
602,900,950,537,616,475,847,728,421,889,679,646,244,945,160,765,353,408,198,901,385,442,487,984,959, 
953,319,101,723,355,556,602,139,450,399,736,280,750,137,837,615,307,127,761,926,849,034,352,625,200, 
015,888,535,147,331,611,702,103,968,175,921,510,907,788,019,393,178,114,194,545,257,223,865,541,461, 
062,892,187,960,223,838,971,476,088,506,276,862,967,146,674,697,562,911,234,082,439,208,160,153,780, 
889,893,964,518,263,243,671,616,762,179,168,909,779,911,903,754,031,274,622,289,988,005,195,444,414, 
282,012,187,361,745,992,642,956,581,746,628,302,955,570,299,024,324,153,181,617,210,465,832,036,786, 
906,117,260,158,783,520,751,516,284,225,540,265,170,483,304,226,143,974,286,933,061,690,897,968,482, 
590,125,458,327,168,226,458,066,526,769,958,652,682,272,807,075,781,391,858,178,889,652,208,164,348, 
344,825,993,266,043,367,660,176,999,612,831,860,788,386,150,279,465,955,131,156,552,036,093,988,180, 
612,138,558,600,301,435,694,527,224,206,344,631,797,460,594,682,573,103,790,084,024,432,438,465,657, 
245,014,402,821,885,252,470,935,190,620,929,023,136,493,273,497,565,513,958,720,559,654,228,749,774, 
011,413,346,962,715,422,845,862,377,387,538,230,483,865,688,976,461,927,383,814,900,140,767,310,446, 
640,259,899,490,222,221,765,904,339,901,886,018,566,526,485,061,799,702,356,193,897,017,860,040,811, 
889,729,918,311,021,171,229,845,901,641,921,068,884,387,121,855,646,124,960,798,722,908,519,296,819, 
372,388,642,614,839,657,382,291,123,125,024,186,649,353,143,970,137,428,531,926,649,875,337,218,940, 
694,281,434,118,520,158,014,123,344,828,015,051,399,694,290,153,483,077,644,569,099,073,152,433,278, 
288,269,864,602,789,864,321,139,083,506,217,095,002,597,389,863,554,277,196,742,822,248,757,586,765, 
752,344,220,207,573,630,569,498,825,087,968,928,162,753,848,863,396,909,959,826,280,956,121,450,994, 
871,701,244,516,461,260,379,029,309,120,889,086,942,028,510,640,182,154,399,457,156,805,941,872,748, 
998,094,254,742,173,582,401,063,677,404,595,741,785,160,829,230,135,358,081,840,096,996,372,524,230, 
560,855,903,700,624,271,243,416,909,004,153,690,105,933,983,835,777,939,410,970,027,753,472,000,000, 
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000, 
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000, 
000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000, 
000,000,000,000,000,000 
2

ho deciso di aggiungere un modo per scegliere il più grande tipo integrale successivo in base alla dimensione del tipo. Controlla se il tipo è firmato e, in caso affermativo, lo rende senza segno. Altrimenti, controlla i tipi senza segno che iniziano con unsigned char per trovare il tipo più piccolo che è più grande di quello corrente. Fallisce un static_assert se non c'è un tipo più grande.Funziona così:

int main() 
{ 
    int a = 0, b = 0; 
    sum(a, b); //return type is unsigned int 
    unsigned int c = 0, d = 0; 
    sum(c, d); //return type is unsigned long long on my platform 
    unsigned long long e = 0, f = 0; 
    sum(e, f); //error 
} 

Il codice completo è di seguito. Non è la cosa più carina del mondo, ma ho pensato che fosse un esperimento interessante.

#include <type_traits> 
#include <tuple> 
#include <cstddef> 

typedef std::tuple<unsigned char, unsigned short, unsigned int, unsigned long, unsigned long long> type_list; 

template <typename, std::size_t> 
struct get_larger_type; 

template <bool B_, std::size_t S_, typename T_, typename... Ts_> 
struct picker 
{ 
    typedef T_ type; 
}; 

template <std::size_t S_, typename T_, typename... Ts_> 
struct picker<false, S_, T_, Ts_...> 
{ 
    typedef typename get_larger_type<std::tuple<Ts_...>, S_>::type type; 
}; 

template <typename T_, typename... Ts_, std::size_t S_> 
struct get_larger_type<std::tuple<T_, Ts_...>, S_> 
{ 
    typedef typename picker<(sizeof(T_) > S_), S_, T_, Ts_...>::type type; 
}; 
template <std::size_t S_> 
struct get_larger_type<std::tuple<>, S_> 
{ 
    //Or if you want to use a multiprecision library, edit this to use that. 
    static_assert(S_ != S_, "Foolish mortal you tread on the domain of gods!"); 
    typedef void type; 
}; 

template <bool, typename T_> 
struct pick_promotion 
{ 
    typedef typename std::make_unsigned<T_>::type type; 
}; 

template <typename T_> 
struct pick_promotion<true, T_> 
{ 
    typedef typename get_larger_type<type_list, sizeof(T_)>::type type; 
}; 

template <typename T_> 
typename pick_promotion<std::is_unsigned<T_>::value, T_>::type sum(T_ a, T_ b) 
{ 
    return static_cast<T_>(a) + static_cast<T_>(b); 
} 
+0

Questo è bello, ingrandito. – vsoftco

1

Espansione alla risposta di @ vsoftco, questo sarebbe un modo più portabile per definire i promote classi, tenendo conto anche il caso in cui entrambi gli argomenti sono negativi:

#include <cstdint> 
#include <type_traits> 
#include <limits> 

template<std::size_t, bool> struct promote; 

template<> struct promote<sizeof(std::uint8_t), false> { using type = std::uint16_t; }; 
template<> struct promote<sizeof(std::uint16_t), false> { using type = std::uint32_t; }; 
template<> struct promote<sizeof(std::uint32_t), false> { using type = std::uint64_t; }; 

template<> struct promote<sizeof(std::int8_t), true> { using type = std::int16_t; }; 
template<> struct promote<sizeof(std::int16_t), true> { using type = std::int32_t; }; 
template<> struct promote<sizeof(std::int32_t), true> { using type = std::int64_t; }; 

template<typename T> using promote_t = typename promote<sizeof(T), std::is_signed<T>::value>::type; 

template <typename T> 
promote_t<T> my_sum(T first, T second) 
{ 
    return static_cast<promote_t<T>>(first) + second; // promotion 
} 

void test() 
{ 
    using namespace std; 
    auto a = numeric_limits<int>::min(); 
    cout << a << " + " << a << " = " << my_sum(a, a) << endl; 

    auto b = numeric_limits<unsigned int>::max(); 
    cout << b << " + " << b << " = " << my_sum(b, b) << endl; 
}