2012-05-05 9 views
12

Mam problem w mojej aplikacji, w którym chciałbym potwierdzić, że aplikacja funkcji zostanie odrzucona przez kompilator. Czy można to sprawdzić za pomocą SFINAE?Jak sprawdzić podczas kompilacji, czy wyrażenie jest niedozwolone?

Załóżmy na przykład, że chciałbym sprawdzić, czy zakres std::transform dla zakresu jest niedozwolony. Oto, co mam do tej pory:

#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; 
} 

Niestety, moja cecha odrzuca zarówno sprawy prawne, jak i nielegalne. Rezultat:

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

'constexpr' może pomóc, tylko szybki myśl. – chris

+0

Innym problemem jest to, że 'std :: cout << sizeof (std :: transform (iter1(), iter1(), iter2(), func()));' kompiluje, podczas gdy 'std :: cout << std :: transform (iter1(), iter1(), iter2(), func()); 'does not. – Lol4t0

+0

@ Lol4t0 To dlatego, że 'sizeof()' nie analizuje argumentów podczas kompilacji. – TemplateRex

Odpowiedz

4

Twoje pytanie jest podobna do SFINAE + sizeof = detect if expression compiles.

Podsumowanie tej odpowiedzi: sizeof ocenia rodzaj ekspresji przekazany do niego, w tym uruchamianiu szablon funkcji, ale nie generuje wywołanie funkcji. To jest powód obserwacji Lol4t0, że sizeof(std::transform(iter1(), iter1(), iter2(), func())) kompiluje, nawet jeśli std::transform(iter1(), iter1(), iter2(), func()) nie.

Twój konkretny problem można rozwiązać, oceniając szablon z odpowiedzi Lol4t0 dla dowolnego zakresu wyjściowego, który ma zostać dostarczony do std::transform. Jednak ogólny problem z weryfikacją w szablonie, który wywołuje wywołanie funkcji, wydaje się niemożliwy do rozwiązania przy pomocy sztuczki sizeof + SFINAE. (wymagałoby to wyrażenia w czasie kompilacji, które można uzyskać z wywołania funkcji czasu działania).

Być może warto wypróbować numer ConceptGCC, aby sprawdzić, czy pozwala to w bardziej wygodny sposób wyrazić wymaganą kontrolę kompilacji.

+0

Twój typ "OutputIterator" może się całkowicie różnić od typu "InputIterator", zależy to od typu zwrotu "Operator": – Lol4t0

+0

@ Lol4t0 To zależy tylko od tego, czy OutIt można przekonwertować na InIt. – TemplateRex

+0

Nie rozumiesz: typ "OutputIterator" może się zupełnie różnić od 'InputIterator' type, ** ale **' std: transform' nadal będzie działać (a twoja walidacja nie powiedzie się). [Przykład] (http://ideone.com/12qfo). – Lol4t0

3

W mojej odpowiedzi chciałbym skupić się na problemie, jak ustalić, czy podana stała iteratora: std::is_const została wspomniana, ale nie działa w tym przypadku dla mnie (gcc 4.7).

Zakładam, że jest implemeted jak

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

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

}; 

Teraz nie można sprawdzić typów referencyjnych z tej struktury, nie będzie pasować do specjalizacji, ponieważ const T pasowałby int& const, czyli stałe odniesienie do int, nie const int&, to jest odniesienie do stałej int, a pierwsze nie mają żadnego sensu.

Ok, ale jesteśmy w stanie określić, czy iterator jest stała ze struktury 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

To jest miła sztuczka. Możesz zamieścić tę odpowiedź na to pokrewne pytanie http://stackoverflow.com/questions/2193399/how-do-i-require-const-iterator-semantics-in-a-template-function-signature (Q ma już 2 lata, ale OP jest nadal aktywny) UPDATE: zobacz także http: // stackoverflow.com/questions/5423246/how-to-detect-if-a-type-is-an-iterator- lub-const-iterator dla nieco innego rozwiązania – TemplateRex

+0

@rhalbersma, dobre linki. naprawiono 'std :: iterator_traits 'problem. :). Zostawię odpowiedź tutaj w kontekście. – Lol4t0

Powiązane problemy