2012-05-05 11 views
12

Ho un problema nella mia applicazione in cui vorrei affermare che un'applicazione di compilazione sarebbe stata rifiutata dal compilatore. C'è un modo per controllare questo con SFINAE?Come verificare in fase di compilazione che un'espressione è illegale?

Ad esempio, supponiamo che mi piacerebbe convalidare che std::transform in un intervallo const non è valido. Ecco cosa ho finora:

#include <algorithm> 
#include <functional> 
#include <iostream> 

namespace ns 
{ 

using std::transform; 

template<typename Iterator1, typename Iterator2, typename UnaryFunction> 
    struct valid_transform 
{ 
    static Iterator1 first1, last1; 
    static Iterator2 first2; 
    static UnaryFunction f; 

    typedef Iterator2     yes_type; 
    typedef struct {yes_type array[2];} no_type; 

    static no_type transform(...); 

    static bool const value = sizeof(transform(first1, last1, first2, f)) == sizeof(yes_type); 
}; 

} 

int main() 
{ 
    typedef int *iter1; 
    typedef const int *iter2; 
    typedef std::negate<int> func; 

    std::cout << "valid transform compiles: " << ns::valid_transform<iter1,iter1,func>::value << std::endl; 

    std::cout << "invalid transform compiles: " << ns::valid_transform<iter1,iter2,func>::value << std::endl; 

    return 0; 
} 

Sfortunatamente, il mio tratto respinge sia i casi legali che quelli illegali. Il risultato:

$ g++ valid_transform.cpp 
$ ./a.out 
valid transform compiles: 0 
invalid transform compiles: 0 
+0

'constexpr' potrebbe aiutare, solo un pensiero veloce. – chris

+0

L'altro problema è che 'std :: cout << sizeof (std :: transform (iter1(), iter1(), iter2(), func()));' compila, mentre 'std :: cout << std :: transform (iter1(), iter1(), iter2(), func()); 'no. – Lol4t0

+0

@ Lol4t0 Questo perché 'sizeof()' non valuta i suoi argomenti durante la compilazione. – TemplateRex

risposta

4

La tua domanda è simile a SFINAE + sizeof = detect if expression compiles.

Sintesi di quella risposta: sizeof valuta il tipo dell'espressione passata ad esso, tra cui un'istanza di un modello di funzione, ma non genera una chiamata di funzione. Questo è il motivo dietro le osservazioni di Lol4t0 che lo sizeof(std::transform(iter1(), iter1(), iter2(), func())) compila anche se non lo è std::transform(iter1(), iter1(), iter2(), func()).

Il tuo problema concreto può essere risolto valutando il modello dalla risposta di Lol4t0 per qualsiasi intervallo di output che deve essere fornito a . Tuttavia, il problema generale di verificare in un modello che una chiamata di funzione verrà compilata sembra impossibile da risolvere con il trucco sizeof + SFINAE. (richiederebbe un'espressione in fase di compilazione derivabile da una chiamata di funzione in fase di esecuzione).

Si consiglia di provare ConceptGCC per verificare se questo consente di esprimere il controllo di compilazione richiesto in un modo più conveniente.

+0

Il tipo 'OutputIterator' può differire completamente dal tipo' InputIterator', dipende dal tipo di ritorno 'Operatore' – Lol4t0

+0

@ Lol4t0 Dipende solo dal fatto che OutIt può essere convertito in InIt. – TemplateRex

+0

Non si ottiene il punto: il tipo 'OutputIterator' può essere completamente diverso dal tipo' InputIterator', ** ma ** 'std: transform' funziona comunque (e la convalida fallirà). [Esempio] (http://ideone.com/12qfo). – Lol4t0

3

Nella mia risposta, I'dd piace concentrarsi su problemi, come determinare, se dato iteratore costante: std::is_const è stato menzionato, ma non funziona in questo caso per me (GCC 4.7).

presumo, si implemeted come

template <typename T> 
struct is_const 
{ 
    enum {value = false }; 
}; 

template <typename T> 
struct is_const<const T> 
{ 
    enum {value = true }; 

}; 

Ora, non si può controllare i tipi di riferimento con questo struct, non sarebbe partita la specializzazione, perché const T sarebbe partita int& const, cioè riferimento costante a int, non const int&, ovvero riferimento a costante int, e in primo luogo non ha alcun senso.

Ok, ma siamo in grado di determinare, se iteratore è costante con la struct follwing:

template <typename Iterator> 
struct is_iterator_constant 
{ 
    typedef char yes_type; 
    typedef struct{ char _[2];} no_type; 
    template <typename T> 
    static no_type test(T&); 

    template <typename T> 
    static yes_type test(...); 

    enum {value = sizeof(test<typename std::iterator_traits<Iterator>::value_type>(*Iterator())) == sizeof(yes_type) }; 

}; 
+1

Questo è un bel trucco.Potresti pubblicare questa risposta a questa domanda correlata http://stackoverflow.com/questions/2193399/how-do-i-require-const-iteratore-semantica-in-a-modello-funzione-firma (Q ha già 2 anni, ma OP è ancora attivo) AGGIORNAMENTO: vedere anche http: // stackoverflow.com/questions/5423246/how-to-detect-if-a-type-is-an-iterator-or-const-iterator per una soluzione leggermente diversa – TemplateRex

+0

@rhalbersma, buoni collegamenti.stato 'std :: iterator_traits 'problema. :). Lascerò la risposta qui per il contesto. – Lol4t0

Problemi correlati