2014-11-14 19 views
12

Sto utilizzando una coppia di parametri del modello intero per specificare un rapporto, poiché non posso utilizzare un doppio come parametro di modello. La conversione in doppio è protetta contro lo split-by-zero con un ternario. Questo ha funzionato in una versione precedente del compilatore, ma Visual Studio 2013 dà un errore:Come eliminare l'errore "dividi per 0" nel codice di modello

error C2124: divide or mod by zero 

Ecco una versione semplificata del codice:

template<int B1, int B2> 
class MyClass 
{ 
    const double B = (B2 == 0) ? 0.0 : (double) B1/(double) B2; 
    // ... 
}; 

MyClass<0, 0> myobj; 

voglio davvero B da ottimizzare di espressioni che lo usa quando è zero, quindi ho bisogno della definizione a linea singola. So che posso semplicemente usare i parametri del modello <0, 1> per aggirare il problema, ma mi chiedo se c'è un modo per convincere il compilatore che la mia espressione è sicura?

+0

Mi chiedo se c'è un modo per mettere questo _diagnostic_ off .... – P0W

+0

@ P0W sfortunatamente è un errore, non un avvertimento. –

+2

Non è stato segnalato da [_'clang'_] (http://rextester.com/FFYYTT36366) né [_'gcc'_] (http://rextester.com/SEDWX49285) – P0W

risposta

15

lavorato Quello che mi hanno detto:

const double B = (B2 == 0 ? 0.0 : (double) B1)/
        (B2 == 0 ? 1.0 : (double) B2); 

Questo evita una dipendenza di valutazione di corto circuito impedendo la divisione per 0; avere le selezioni condizionali avvengono prima della divisione.


Idea originale/Forse qualcosa di simile ...? (Credo B dovrebbe essere static const o constexpr, ma sono sicuro che è possibile ordinare che ...)

template<int B1, int B2> 
struct MyClass 
{ 
    const double B = (double) B1/(double) B2; 
}; 

template <int B1> 
struct MyClass<B1, 0> 
{ 
    const double B = 0.0; 
}; 

Se c'è un sacco di altre cose che si desidera in MyClass e non si desidera duplicare o mettere in una base ecc., è possibile spostare il calcolo B in un modello di supporto utilizzando l'approccio di specializzazione sopra.

+0

Nella mia classe attuale ho * due * rapporti, quindi diventa un po 'più complicato. Ma potrebbe essere possibile creare una classe di modelli di rapporto e utilizzare qualcosa come "Rapporto <0, 0> :: valore". Dovrò giocarci un po '. –

+0

Spero non ti dispiaccia se questo esperimento attende. È mezzanotte qui e mi sono appena trasformato in una zucca. –

+0

@MarkRansom: certo che no ... vacci piano! BTW: se pensi che sia più facile, potresti provare: const double B = (B2 == 0? 0.0: (double) B1)/(B2 == 0? 1.0: (double) B2); Saluti. –

2

Visual Studio non è in grado di eseguire il typecast B1, B2 in operazione ternaria in fase di compilazione, ma il casting esplicito funzionerà.

template<int B1, int B2> 
class MyClass 
{ 
    double d1 = (double)B1; 
    double d2 = (double)B2; 
    const double B = (B2 == 0) ? 0.0 : d1/d2; 
    // ... 
}; 

MyClass<0, 0> myobj; 
+0

Ma il compilatore riconosce 'B' come costante in fase di compilazione? Avresti bisogno di vedere il codice assembly generato per sapere con certezza. –

+0

sì, dovrebbe essere la costante di tempo di compilazione. –

+0

Versione di questa risposta [qui] (http://ideone.com/TGwKt5) - con 'static constexpr' - funziona per gcc ed è costante in fase di compilazione. +1. Saluti. –

2

Per i curiosi: ecco il codice con cui ho finito. Probabilmente aiuta a vederlo in un contesto reale.

template<int B1, int B2, int C1, int C2> 
class BicubicFilter 
{ 
    // Based on the formula published by Don Mitchell and Arun Netravali at 
    // http://www.cs.utexas.edu/~fussell/courses/cs384g-fall2013/lectures/mitchell/Mitchell.pdf 
public: 
    BicubicFilter() : m_dMultiplier(1.0) {} 
    double m_dMultiplier; 
    double k(double x) const 
    { 
     const double B = (double) B1/((B2 == 0) ? 1.0 : (double) B2); 
     const double C = (double) C1/((C2 == 0) ? 1.0 : (double) C2); 
     x = fabs(x) * m_dMultiplier; 
     if (x < 1.0) 
      return ((2.0 - 1.5*B - C) * x*x*x) + ((-3.0 + 2.0*B + C) * x*x) + (1.0 - (2.0/6.0)*B); 
     if (x < 2.0) 
      return (((-1.0/6.0)*B - C) * x*x*x) + ((B + 5.0*C) * x*x) + ((-2.0*B - 8.0*C) * x) + ((8.0/6.0)*B + 4.0*C); 
     return 0.0; 
    } 
}; 

La funzione k viene eseguita almeno 4 volte per pixel di un'immagine ridimensionamento operazione, per cui l'efficienza è critica. Voglio che tutte le costanti siano conosciute al momento della compilazione, in modo che il compilatore possa semplificare il più possibile le espressioni.

Sulla base della risposta accettata, avevo sperato di creare una classe Ratio modello che sarebbe semplicemente produrre il rapporto di due int s come constexpr double e specializzati per i parametri di 0, 0. Visual Studio 2013 non implementa ancora constexpr quindi non ero sicuro che il compilatore lo considerasse una costante in fase di compilazione. Fortunatamente una variazione sull'espressione ternaria originale ha eliminato l'errore.

+0

Sembra voodoo. Qualche idea sul perché questa espressione è ottimizzata, ma quella originale no? – anatolyg

+0

@ anatolyg Penso che la domanda migliore sia il motivo per cui l'espressione originale ha fallito; entrambi dovrebbero logicamente evitare il calcolo dello zero per zero, e altri riportano che gcc e clang non hanno alcun problema con esso. Sospetto che Visual Studio stia valutando il lato destro del ternario prima che determini che ne ha bisogno. Nella nuova formula il lato destro non contiene un errore. –

Problemi correlati