2012-08-16 14 views
19

Ho utilizzato (e visto usato) static_assert per segnalare valori non desiderati dei valori dei parametri del modello. Tuttavia, per tutti i casi in cui mi sono imbattuto sembra meglio e più elegante disabilitare quei valori indesiderati tramite SFINAE. Per esempioQuando utilizzare `static_assert` invece di SFINAE?

template<typename T, 
     class = std::enable_if<std::is_floating_point<T>::value>::type> 
struct Foo { ... }; 

invece di

template<typename T> 
struct Foo { 
    static_assert(std::is_floating_point<T>::value, 
       "Foo<T>: T must be floating point :-("); 
    ... 
}; 

Quindi la mia domanda: quando usare static_assert invece di SFINAE e perché?

EDIT

Penso che quello che ho imparato finora è il seguente

SFINAE è uno strumento versatile e potente, ma potenzialmente molto complicato che può essere utilizzato per molte attività, tra cui funzione di risoluzione di sovraccarico (che alcuni sembrano considerare come il suo unico scopo).

SFINAE può essere utilizzato in modo relativamente semplice in cui mai static_assert può, tranne che appare nel dichiarazione (di una classe o funzione) piuttosto che la sua definizione (o è possibile inserire a static_assert in una dichiarazione di inoltro di classe?). Questo rende il codice più verbale e quindi più chiaro. Tuttavia, poiché SFINAE è complicato, tende ad essere più difficile da ottenere rispetto ad un semplice static_assert.

D'altra parte static_assert ha il vantaggio di un messaggio di errore del compilatore più chiaro, che alcuni sembrano considerare come lo scopo principale di entrambi.

+3

Puoi spiegare perché pensi che sia meglio con SFINAE? –

+0

Forse le nostre risposte sono orientate dal vocabolario. Nella tua domanda penso che dovresti sostituire SFINAE con 'std :: enable_if'. Sembra strano voler generare un errore con un meccanismo che enfatizza il fatto che non si tratta di un errore. SFINAE = Errore di sostituzione non è un errore – log0

risposta

6

penso static_assert è il scelta giusta se si vuole far rispettare che T è un tipo a virgola mobile. Questo metodo dichiara chiaramente le tue intenzioni rispetto alla soluzione SFINAE.

+0

Non sono d'accordo. considera la dichiarazione anticipata 'modello class some_class;'. Come posso sapere che quella classe accetta solo i parametri del modello di tipo integrale se 'static_assert' viene solo nella definizione della classe? – Walter

+0

@Walter SFINAE non è applicabile in questo caso, quindi come è importante? In ogni caso con un po 'di ginnastica puoi scrivere qualcosa come 'template > ...> class some_class;' (dove 'Requires' è un trucco come quelli usati in Boost.Concepts), anche se non posso garantire la legittimità (e la portabilità) di una cosa del genere. –

7

static_assert rende la compilazione non riuscita. SFINAE ti consente di rimuovere un possibile sovraccarico.

+0

Nel caso illustrato, entrambi rendono la compilazione fallita se si scrive 'Foo '. –

+0

SFINAE disabilita un determinato parametro del modello.Se il compilatore non riesce a trovare un'altra corrispondenza, la compilazione fallirà. – Walter

+0

@ R.MartinhoFernandes Ho appena letto il tuo blog/pagina su * Remastered enable_if * e mancava la definizione del modello 'all <>' usato per combinare diversi tipi di condizione. Dove posso trovarlo? – Walter

12

Si utilizza SFINAE, se si desidera utilizzare un altro sovraccarico e static_assert se nessuno di questi si adatta a tale parametro.

5

Per prima cosa, l'uso di SFINAE può determinare un altro sovraccarico che originariamente era una corrispondenza peggiore e che non verrebbe considerato.

E nella situazione che ci sono altri sovraccarichi, ma non di loro è vitale, si ottiene alcune cose belle in questo modo:

#include <type_traits> 

void f(int){} 
void f(bool){} 
void f(char){} 
void f(float){} 
void f(long){} 
void f(double){} 
void f(short){} 
void f(unsigned){} 
void f(void*){} 
void f(void (*)()){} 

template<class C, class T = int> 
using EnableIf = typename std::enable_if<C::value, T>::type; 

template<class T> 
struct sfinae_false : std::false_type{}; 

template<class T> 
void f(T&&, EnableIf<sfinae_false<T>> = 0){} 

int main(){ struct X{}; f(X()); } 

uscita:

source.cpp: In function 'int main()': 
source.cpp:23:30: error: no matching function for call to 'f(main()::X)' 
source.cpp:23:30: note: candidates are: 
source.cpp:3:6: note: void f(int) 
source.cpp:3:6: note: no known conversion for argument 1 from 'main()::X' to 'int' 
source.cpp:4:6: note: void f(bool) 
source.cpp:4:6: note: no known conversion for argument 1 from 'main()::X' to 'bool' 
source.cpp:5:6: note: void f(char) 
source.cpp:5:6: note: no known conversion for argument 1 from 'main()::X' to 'char' 
source.cpp:6:6: note: void f(float) 
source.cpp:6:6: note: no known conversion for argument 1 from 'main()::X' to 'float' 
source.cpp:7:6: note: void f(long int) 
source.cpp:7:6: note: no known conversion for argument 1 from 'main()::X' to 'long int' 
source.cpp:8:6: note: void f(double) 
source.cpp:8:6: note: no known conversion for argument 1 from 'main()::X' to 'double' 
source.cpp:9:6: note: void f(short int) 
source.cpp:9:6: note: no known conversion for argument 1 from 'main()::X' to 'short int' 
source.cpp:10:6: note: void f(unsigned int) 
source.cpp:10:6: note: no known conversion for argument 1 from 'main()::X' to 'unsigned int' 
source.cpp:11:6: note: void f(void*) 
source.cpp:11:6: note: no known conversion for argument 1 from 'main()::X' to 'void*' 
source.cpp:12:6: note: void f(void (*)()) 
source.cpp:12:6: note: no known conversion for argument 1 from 'main()::X' to 'void (*)()' 
source.cpp:21:6: note: template<class T> void f(T&&, EnableIf<sfinae_false<T> >) 
source.cpp:21:6: note: template argument deduction/substitution failed: 
+2

Una cattiva proprietà di SFINAE è che è necessario assicurarsi che più sovraccarichi avere condizioni non sovrapposte, altrimenti si ottiene comunque un errore. Preferisco di gran lunga l'invio dei tag per l'overloading selettivo. – TemplateRex

+1

Dato che l'esempio nella domanda è un modello di classe piuttosto che un modello di funzione, questa risposta equivale a "sei corretto per le classi, ma non hai considerato le funzioni"? O ci sono vantaggi anche per le asserzioni statiche per le classi? –

+0

@SteveJessop: Beh, SFINAE non si applica realmente ai modelli di classe, poiché * è * un errore grave ("nessun membro di nome 'tipo' in 'std :: enable_if ' ...). – Xeo

Problemi correlati