2012-05-05 14 views
12

Tengo un problema en mi aplicación en el que me gustaría afirmar que el compilador rechazará una aplicación de función. ¿Hay alguna manera de verificar esto con SFINAE?¿Cómo comprobar en tiempo de compilación que una expresión es ilegal?

Por ejemplo, supongamos que me gustaría validar que std::transform a un rango const es ilegal. Esto es lo que tengo hasta ahora:

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

Lamentablemente, mi rasgo rechaza tanto los casos legales como los ilegales. El resultado:

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

'constexpr' podría ayudar, sólo un pensamiento rápido. – chris

+0

El otro problema es que 'std :: cout << sizeof (std :: transform (iter1(), iter1(), iter2(), func()));' compila, mientras que 'std :: cout << std :: transform (iter1(), iter1(), iter2(), func()); 'no. – Lol4t0

+0

@ Lol4t0 Eso es porque 'sizeof()' no evalúa sus argumentos durante el tiempo de compilación. – TemplateRex

Respuesta

4

Su pregunta es similar a SFINAE + sizeof = detect if expression compiles.

Resumen de esa respuesta: sizeof evalúa el tipo de la expresión se le ha pasado, incluyendo una instancia de una plantilla de función, pero no genera una llamada a la función. Esta es la razón detrás de las observaciones de Lol4t0 que compila sizeof(std::transform(iter1(), iter1(), iter2(), func())) incluso si std::transform(iter1(), iter1(), iter2(), func()) no lo hace.

Su problema concreto se puede resolver evaluando la plantilla de la respuesta de Lol4t0 para cualquier rango de salida que se debe suministrar a std::transform. Sin embargo, el problema general de verificar en una plantilla compilada por una llamada a función parece ser imposible de resolver con el truco sizeof + SFINAE. (requeriría una expresión de tiempo de compilación derivable de una llamada de función en tiempo de ejecución).

Es posible que desee probar ConceptGCC para ver si esto le permite expresar la verificación necesaria en tiempo de compilación de una manera más conveniente.

+0

Su tipo 'OutputIterator' puede diferir completamente del tipo 'InputIterator', depende de' Operator' return type – Lol4t0

+0

@ Lol4t0 Solo depende de si OutIt puede convertirse a InIt. – TemplateRex

+0

No entiendo el punto: el tipo 'OutputIterator' puede ser completamente diferente del tipo' InputIterator', ** pero ** 'std: transform' aún funcionaría (y su validación fallará). [Ejemplo] (http://ideone.com/12qfo). – Lol4t0

3

En mi respuesta, me gustaría enfocarme en el problema, cómo determinar, si se da la constante del iterador: std::is_const se mencionó, pero en este caso no funciona para mí (gcc 4.7).

supongo, se implementado algunas como

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

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

}; 

Ahora, no se puede comprobar los tipos de referencia con esta estructura, que no se correspondería con la especialización, porque const T coincidiría int& const, es decir referencia constante a int, no const int&, que es referencia a la constante int, y en primer lugar no tiene ningún sentido.

Ok, pero somos capaces de determinar, si iterador es constante con la estructura siguientes aparatos:

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

Este es un buen truco. Es posible que desee publicar esta respuesta a esta pregunta relacionada http://stackoverflow.com/questions/2193399/how-do-i-require-const-iterator-semántica-in-a-template-function-signature (Q ya tiene 2 años, pero OP todavía está activo) ACTUALIZACIÓN: ver también http: // stackoverflow.com/questions/5423246/how-to-detect-if-a-type-is-an-iterator-or-const-iterator para una solución ligeramente diferente – TemplateRex

+0

@rhalbersma, buenos enlaces. fixed 'std :: iterator_traits 'problema. :). Dejaré la respuesta aquí para el contexto. – Lol4t0

Cuestiones relacionadas