2013-06-07 1 views
16

Questo problema è stato discusso alcune volte ma tutte le soluzioni che ho trovato o non funzionavano o erano basate sull'asserzione statica di boost. Il mio problema è semplice Ho una classe e voglio solo consentire i tipi reali (double e float). Voglio un errore in fase di compilazione se provo a istanziare la classe con un tipo diverso da float o double. Sto usando Visual C++ 11. Ecco cosa ho provato:Come si limita una classe template ad alcuni tipi built-in?

template <typename RealType> 
class A 
{ 
    // Warning C4346 
    static_assert(std::is_same<RealType, double>::value || std::is_same<RealType, float>::value); 
} 


template <typename RealType> 
class A 
{ 
    // Error C2062: type 'unknown' unexpected 
    static_assert(decltype(RealType) == double || decltype(RealType) == float); 
} 

Qualche idea? Grazie in anticipo!

+1

La prima domanda è, cosa importa? Se si crea un'istanza del modello con un altro tipo e il tipo non può essere trattato nel modo in cui il modello si aspetta di trattarlo, la compilazione avrà esito negativo. E se è possibile, permettendo * solo * quei due tipi effettivamente fuorilegge ... diciamo ... un tipo come BigDecimal. – cHao

+0

Visual Studio genera davvero un avviso quando static_assert fallisce, e non un errore? Sembra un insetto. –

+0

Vedere anche [Modelli C++ che accettano solo determinati tipi] (http://stackoverflow.com/q/874298/) e [Limita i parametri del modello C++ alla sottoclasse] (http://stackoverflow.com/q/3175219).Precedono C++ 11, ma potrebbero essere un buon indicatore per qualcun altro. – jww

risposta

12

Una soluzione che ho visto è utilizzare std::enable_if in un alias di tipo. Qualcosa di simile:

using value_type = typename std::enable_if< 
        std::is_same<float, RealType>::value || 
        std::is_same<double, RealType>::value, 
        RealType 
       >::type; 

value_type esiste solo se RealType è esattamente float o double. In caso contrario, il tipo non è definito e la compilazione non riesce.

Avrei avvertito di essere troppo severo con i tipi, però. I modelli sono tanto potenti quanto in parte perché la digitazione anatra che fanno significa che qualsiasi tipo che può essere utilizzato nel modo in cui si desidera utilizzarlo funzionerà. Disabilitare i tipi al solo scopo di disabilitare i tipi generalmente non ti fa guadagnare molto e può rendere le cose meno flessibili di quanto potrebbero essere. Ad esempio, non si sarebbe in grado di utilizzare un tipo con maggiore precisione, come un tipo decimale grande.

+0

Questo, e se avete bisogno solo di due specializzazioni, il buon vecchio OO e il polimorfismo di runtime potrebbero essere un'idea migliore (almeno vale la pena di considerarla). –

+0

SFINAE è veramente buono quando vuoi creare più specializzazioni del modello in seguito :) Tuttavia, se è sicuro che non ci sarà mai altra specializzazione, 'static_assert' consente di visualizzare messaggi di errore significativi (perché quelli di SFINAE spesso mi fanno male agli occhi). – Morwenn

26

Nel vostro primo esempio, static_assert dovrebbe prendere un secondo parametro che sarebbe una stringa letterale, altrimenti è considerato a fallire (edit: far cadere il secondo parametro è legale dal C++ 17). E questo secondo argomento non può essere predefinito.

Il secondo esempio è corretto per diversi motivi:

  • decltype deve essere utilizzato in un'espressione, non su un tipo.
  • Non è possibile confrontare i tipi con ==, il modo corretto per farlo è quello che si prova al primo tentativo con std::is_same.

Quindi, il modo giusto per fare ciò che si sta cercando di raggiungere è:

#include <type_traits> 

template <typename RealType> 
class A 
{ 
    static_assert(std::is_same<RealType, double>::value || std::is_same<RealType, float>::value, 
       "some meaningful error message"); 
}; 

Inoltre, scommetto che sta cercando di costringere il modello di valori punti galleggianti. Per fare questo, è possibile utilizzare il tratto std::is_floating_point:

#include <type_traits> 

template <typename RealType> 
class A 
{ 
    static_assert(std::is_floating_point<RealType>::value, 
       "class A can only be instantiated with floating point types"); 
}; 

E come bonus, prendere this online example.

5

In questo modo si consente anche la specializzazione di vari tipi:

template<typename T, typename Enable = void> 
class A { 
/// Maybe no code here or static_assert(false, "nice message"); 
}; 

/// This specialization is only enabled for double or float. 
template<typename T> 
class A<T, typename enable_if<is_same<T, double>::value || is_same<T, float>::value>::type> { 

}; 
+1

con 'static_assert (false," nice message ");' il programma è mal formato, nessuna diagnostica richiesta. Vedi http://stackoverflow.com/questions/30078818/static-assert-dependent-on-non-type-template-parameter-different-behavior-on-gc Il problema lì a prima vista sembra più complesso, ma la causa della sottolineatura è semplice come il tuo esempio. – bolov