2012-04-12 11 views
6

Ho giocato in giro con C++ 11 ultimamente, e si avvicinò con la seguente funzione somma:Il compilatore non avvisa sulla perdita di precisione?

template <typename T> 
inline T sum(const std::function<T (int)> &f, int initial, int end) 
{ 
    T retval = 0; 
    for(int k = initial; k <= end; k++) { 
     retval += f(k); 
    } 
    return retval; 
} 

L'idea è che posso passare una funzione lambda e quindi avere una funzione di pulito e leggibile per matematica somme. Allora ho provato la seguente:

int main() 
{ 
    std::array<double, 2> arr1 = {{ 0.5, 1.5 }}, 
          arr2 = {{ 1.5, -0.5 }}; 
    auto n = sum<int>([&](int k) { return arr1[k]*arr2[k]; }, // Precision loss! 
         0, 1); 
    return 0; 
} 

ho compilato questo usando g ++ 4.6.3: g++ -Wall -pedantic -std=c++0x -o test main.cpp e non dà alcun avviso circa la perdita di precisione ho osservato nel commento del codice sorgente.

È praticamente un dato di fatto che sum<int> è una brutta cosa da fare, ma potrebbe non essere così ovvio in contesti più complessi. Il compilatore non dovrebbe notare che il valore di ritorno della mia funzione lambda è double e mi avverte che sto perdendo precisione quando lancio a int? C'è una ragione specifica che non lo fa?

+1

La funzione non è a conoscenza della precisione perché il runtime esegue un cast implicito prima di passare gli argomenti. Hai ragione, però, in un mondo ideale dovresti almeno vedere un avvertimento in Visual Studio/IDE per riflettere sul fatto che ciò comporterebbe una perdita di precisione. ma non è questo tipo di cosa cosa sono i test unitari? – War

+0

@Wardy in un mondo ideale non sarebbe compilato (cioè sarebbe un errore, non un avvertimento) senza una richiesta esplicita di causare questa perdita di precisione. –

+0

ok ... beh, io sono uno sviluppatore di C# e devo seguire le regole di .net (non sono sicuro se si sta utilizzando anche un compilatore .net ma) afferma nella documentazione che dove è previsto un tipo meno preciso implicito il cast è applicato ... è considerato una caratteristica della lingua. Sono d'accordo però ... un errore sarebbe il sogno assoluto nel tuo scenario ma non comune alla piattaforma nella mia esperienza. – War

risposta

5

Sembra del tutto ragionevole. Stai dicendo al compilatore di non preoccuparsi di Template Argument Deduction (ad esempio, usa sum<double>) ma invece di dirlo esplicitamente per usare sum<int>. È buono come un cast esplicito.

[Edit] Che dire

template <typename F> 
auto sum(F const& f, int initial, int end) -> decltype(f(initial)) 
{ 
    auto retval = f(initial++); 
    while(initial <= end) { 
     retval += f(initial++); 
    } 
    return retval; 
} 
+0

È abbastanza strano se gli avvertimenti relativi alle istruzioni nella funzione _implementazione_ dipendono da un _way_ in cui sono stati dedotti argomenti del template – user396672

+0

@ user396672: a quale "implementazione della funzione" si riferisce? Non c'è una dichiarazione in 'somma (const std :: function & f, ...)' che garantisce un avvertimento. – MSalters

+0

return (cast implicito in int) arr1 [k] * arr2 [k]; – user396672

2

VS11 fa emettere un avvertimento:

Attenzione C4189 1 avvertimento: 'n': variabile locale viene inizializzata, ma non fa riferimento
Attenzione C4244 2 avvertimento: '+ =': conversione da 'doppio' a 'int', possibile perdita di dati

Modifica, in realtà l'avvertimento viene dall'uso del codice:

template <typename T,typename Func> 
inline T sum(Func f, int initial, int end) 

Viene visualizzato un avviso diverso sulla conversione errata se si utilizza std::function<T (int)>, quindi VS è ancora valido su questo problema. (IMO, in genere dovresti prendere funtori come tipo di modello piuttosto che std :: function)

Anche clang con -Weverything non emette un avviso a riguardo (Modifica: anche se non riesco a testare la funzione std :: versione con clang ATM). Sembra qualcosa che potrebbe essere migliorato.

io capisco questo strano avvertimento però:

ConsoleApplication1.cpp:15:51: warning: will never be executed [-Wunreachable-code] 
    auto n = sum<int>([&](int k) { return arr1[k]*arr2[k]; }, // Precision loss! 
                ^~~~ 
1

Se Sblocca il funtore e se si cambia la linea retval += f(k); al retval += T { f(k) }; nel seguente modo:

// Machinery to allow caller to indifferently use 
// sum(f, i, j) and sum<R>(f, i, j) 
struct deduced {}; 

template< 
    typename Request = deduced 
    , typename Functor 
    , typename Ret = typename std::conditional< 
     std::is_same<Request, deduced>::value 
     , typename std::result_of<Functor&(int)>::type 
     , Request 
    >::type 
> 
inline Ret sum(Functor f, int initial, int end) 
{ 
    Ret retval = 0; 
    for(int k = initial; k <= end; k++) { 
     retval += Ret { f(k) }; 
    } 
    return retval; 
} 

poi invece di basarsi su la volontà del compilatore di avvisare, lo fai richiesto per emettere una diagnostica in quanto una restrizione della conversione non è consentita all'interno di inizializzazione dell'elenco (es. inizializzazione con parentesi).

Non credo che ci sia un modo affidabile se si vincola il functor a std::function<Sig>. Ciò dipende esclusivamente dal modo in cui l'implementazione ha scritto std::function, quando emette avvisi per restringere le conversioni e anche se avvisa del proprio codice.

Problemi correlati