2012-01-05 9 views
7

Esto es solo una pregunta sobre el estilo: no me gusta el camino de C++ para la metaprogramación de plantillas que requiere usar el tipo de retorno o agregar un argumento ficticio adicional para los trucos con SFINAE . Por lo tanto, la idea que se me ocurrió es poner la cosa SFINAE en la definición argumentos de plantilla en sí, así:boost :: enable_if no en la firma de función

#include <iostream> 
#include <boost/type_traits/is_array.hpp> 
#include <boost/utility/enable_if.hpp> 
using namespace std; 

template <typename T, typename B=typename boost::enable_if< boost::is_array<T> >::type > void asd(){ 
    cout<<"This is for arrays"<<endl; 
} 

template <typename T, typename B=typename boost::disable_if< boost::is_array<T> >::type > void asd(){ 
    cout<<"This is for NON arrays"<<endl; 
} 

int main() { 
    asd<int>(); 
    asd<int[]>(); 
} 

En este ejemplo se crea g ++ se quejan:

../src/afg.cpp:10:97: error: redefinition of ‘template void asd()’

SFINAE existe en sí funciona, porque si elimino por ejemplo, el que tiene disable_if, el error del compilador es:

../src/afg.cpp:15:12: error: no matching function for call to ‘asd()’

¿qué es lo que quiero.

Entonces, ¿hay alguna forma de lograr SFINAE no en la firma "normal" de una función, es decir, tipo de retorno + lista de argumentos?

EDIT: Esto es al final lo que voy a tratar en el código real:

#include <iostream> 
#include <type_traits> 
using namespace std; 

template <typename T, typename enable_if< is_array<T>::value, int >::type =0 > void asd(){ 
    cout<<"This is for arrays"<<endl; 
} 

template <typename T, typename enable_if< !is_array<T>::value, int >::type =0 > void asd(){ 
    cout<<"This is for NON arrays"<<endl; 
} 

int main() { 
    asd<int[]>(); 
    asd<int>(); 
} 

utilizo C++ 0x cosas en lugar de impulso, porque el tiempo que necesito C++ 0x para usar los valores predeterminados de los argumentos de la plantilla, no veo ninguna razón para usar boost, que es su precursor.

Respuesta

6

argumentos de plantilla por defecto no son parte de la firma de plantillas de función. Pero el tipo de parámetros de plantilla es.Así que usted puede hacer lo siguiente y ser capaz de sobrecargarla

template < 
    typename T, 
    typename boost::enable_if< 
    boost::is_array<T>, int 
    >::type = 0 
> 
void asd() { 
    cout<<"This is for arrays"<<endl; 
} 

template < 
    typename T, 
    typename boost::disable_if< 
    boost::is_array<T>, int 
    >::type = 0 
> 
void asd() { 
    cout<<"This is for arrays"<<endl; 
} 
+0

wow, yo no sabía que incluso el tipo de la plantilla podría ser dinámicos No aceptar su nuestra respuesta todavía porque primero tengo que comprobar que este truco se puede aplicar a mi código, pero estoy bastante seguro de que lo hará. –

8

Dado que C++ 11 lo hizo posible, solo uso enable_if (o por el contrario disable_if) dentro de los argumentos de la plantilla, de la manera que lo está haciendo. Si/cuando hay varias sobrecargas, entonces utilizo argumentos de plantilla predeterminados que hacen que las listas de parámetros de la plantilla difieran en arity. Así que para su reutilización ejemplo, que sería:

template< 
    typename T 
    , typename B = typename boost::enable_if< 
     boost::is_array<T> 
    >::type 
> 
void asd() { 
    cout << "This is for arrays" << endl; 
} 

template< 
    typename T 
    , typename B = typename boost::disable_if< 
     boost::is_array<T> 
    >::type 
    , typename = void 
> 
void asd() { 
    cout << "This is for arrays" << endl; 
} 

Otra alternativa para no ensuciar el tipo de retorno (que no está disponible en algunos casos, por ejemplo, operadores de conversión) que existe desde C++ 03 es utilizar argumentos por defecto :

template<typename T> 
void 
foo(T t, typename std::enable_if<some_trait<T>::value>::type* = nullptr); 

yo no uso esta forma como me gusta 'jugar' con los tipos de argumentos, tanto como con el tipo de retorno, y por razones de coherencia (ya que no es factible en todos los casos).

2

Puede que esto no sea exactamente lo que estás pidiendo, pero ¿qué hay de la buena especialización de plantillas antiguas?

template<typename T> 
struct asd 
{ 
    static void fgh() 
    { 
     std::cout << "not an array\n"; 
    } 
}; 

template<typename T> 
struct asd<T[]> 
{ 
    static void fgh() 
    { 
     std::cout << "an array of unknown size\n"; 
    } 
}; 

template<typename T, size_t N> 
struct asd<T[N]> 
{ 
    static void fgh() 
    { 
     std::cout << "an array of known size\n"; 
    } 
}; 

int main() 
{ 
    asd<int>::fgh(); 
    asd<int[]>::fgh(); 
    asd<int[42]>::fgh(); 
} 
+0

bueno, pero es más largo que la otra respuesta. –

+3

Al menos imprime cosas diferentes para matrices y no matrices ;-) – fredoverflow

2

So, is there a way to accomplish SFINAE not in the "normal" signature of a function, that is return type + argument list?

Bueno, hay una manera de obtener el mismo resultado sin utilizar SFINAE en absoluto — sobrecarga:

#include <iostream> 
#include <type_traits> 

void asd_impl(std::true_type&&) 
{ 
    std::cout << "This is for arrays\n"; 
} 

void asd_impl(std::false_type&&) 
{ 
    std::cout << "This is not for arrays\n"; 
} 

template<typename T> 
void asd() 
{ 
    asd_impl(std::is_array<T>()); 
} 

int main() 
{ 
    asd<int>(); 
    asd<int[]>(); 
} 

Este estilo es mucho más fácil de leer la OMI, y se utiliza ampliamente en plantilla- bibliotecas pesadas como Boost. Spirit porque tiende a compilarse más rápido y funciona mejor con compiladores que tienen una plantilla menos que estelar/compatibilidad SFINAE (por ejemplo, VC++ y Sun Studio).

Online demo.

+0

+1, para preguntas simples verdadero/falso, uno no necesita emplear SFINAE. Solo se pondrá peludo para más de un rasgo, ya que no puedes simplemente combinarlos con operadores lógicos. (Lo que hace que me pregunte si se puede sobrecargar el operador || '' y '' operador && para '' true_type' y false_type' ... hm.) – Xeo

+0

@Xeo: 'std :: integral_constant 'debe tener la semántica adecuada. – ildjarn

+0

Sí, pero parece un poco desordenado, es por eso que me preguntaba sobre la sobrecarga del operador. :) Básicamente haría que solo se vea mejor. – Xeo

8

Bueno, en general, utilizar estas macros para hacer enable_if construye mucho más limpio (incluso funcionan en la mayoría de los compiladores de C++ 03):

#define ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE(...) __VA_ARGS__>::type 
#define FUNCTION_REQUIRES(...) typename boost::enable_if<boost::mpl::and_<__VA_ARGS__, boost::mpl::bool_<true> >, ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE 
#define EXCLUDE(...) typename boost::mpl::not_<typename boost::mpl::or_<__VA_ARGS__, boost::mpl::bool_<false> >::type >::type 

allí tendría que definir su función como esta:

template <typename T > 
FUNCTION_REQUIRES(is_array<T>) 
(void) asd(){ 
    cout<<"This is for arrays"<<endl; 
} 

template <typename T > 
FUNCTION_REQUIRES(EXCLUDE(is_array<T>)) 
(void) asd(){ 
    cout<<"This is for NON arrays"<<endl; 
} 

la única cosa es que usted necesita para poner paréntesis alrededor del tipo de retorno. Si los olvida, el compilador dirá algo como 'ERROR_PARENTHESIS_MUST_BE_PLACED_AROUND_THE_RETURN_TYPE' no está definido.

Cuestiones relacionadas