2010-01-24 17 views
5

Me preguntaba si es posible extender el enfoque SFINAE para detectar si una clase tiene una cierta función miembro (como se discute aquí:¿Cómo determinar si una clase tiene una función miembro particular con plantilla?

"¿Hay una técnica en C++ para saber si una clase tiene una función miembro de una ¿Firma dada? " Check if a class has a member function of a given signature

) para admitir funciones de miembro modelado? P.ej. para ser capaz de detectar la función foo en la clase siguiente:

struct some_class { 
    template < int _n > void foo() { } 
}; 

pensé que podría ser posible hacer esto para una instanciación particular de foo, (por ejemplo, comprobar para ver si void foo<5>() es un miembro) como sigue:

template < typename _class, int _n > 
class foo_int_checker { 

    template < typename _t, void (_t::*)() > 
    struct sfinae { }; 

    template < typename _t > 
    static big 
    test(sfinae< _t, &_t::foo<_n> > *); 

    template < typename _t > 
    static small 
    test(...); 

public: 

    enum { value = sizeof(test<_class>(0)) == sizeof(big) }; 

}; 

Después, realice foo_int_checker< some_class, 5 >::value para comprobar si some_class tiene el miembro void foo<5>(). Sin embargo, en MSVC++ 2008 esta siempre devuelve false mientras g ++ da los siguientes errores de sintaxis en la línea test(sfinae< _t, &_t::foo<_n> >);

test.cpp:24: error: missing `>' to terminate the template argument list 
test.cpp:24: error: template argument 2 is invalid 
test.cpp:24: error: expected unqualified-id before '<' token 
test.cpp:24: error: expected `,' or `...' before '<' token 
test.cpp:24: error: ISO C++ forbids declaration of `parameter' with no type 

Ambos parecen fallar porque estoy tratando de obtener la dirección de una instanciación función de plantilla a partir de un tipo que es en sí misma un parámetro de plantilla. ¿Alguien sabe si esto es posible o si no está permitido por el estándar por alguna razón?

EDITAR: Parece que me perdí la sintaxis ::template para obtener g ++ para compilar el código anterior correctamente. Si cambio el bit donde obtengo la dirección de la función a &_t::template foo<_n>, entonces el programa se compila, pero obtengo el mismo comportamiento que MSVC++ (value siempre se establece en false).

Si comento hacia fuera de la sobrecarga de ...test para forzar al compilador para recoger el otro, me sale el siguiente error del compilador en g ++:

test.cpp: In instantiation of `foo_int_checker<A, 5>': 
test.cpp:40: instantiated from here 
test.cpp:32: error: invalid use of undefined type `class foo_int_checker<A, 5>' 
test.cpp:17: error: declaration of `class foo_int_checker<A, 5>' 
test.cpp:32: error: enumerator value for `value' not integer constant 

donde la línea 32 es la línea enum { value = sizeof(test<_class>(0)) == sizeof(big) };. Desafortunadamente, esto no parece que me ayude a diagnosticar el problema :(MSVC++ da un error anodino similar:..

error C2770: invalid explicit template argument(s) for 'clarity::meta::big checker<_checked_type>::test(checker<_checked_type>::sfinae<_t,&_t::template foo<5>> *)' 

en la misma línea

Lo extraño es que si me da la dirección de una específica clase y no un parámetro de plantilla (es decir, en lugar de &_t::template foo<_n> hago &some_class::template foo<_n>) luego obtengo el resultado correcto, pero entonces mi clase checker se limita a verificar una sola clase (some_class) para la función. Además, si hago lo siguiente:

template < typename _t, void (_t::*_f)() > 
void 
f0() { } 

template < typename _t > 
void 
f1() { 
    f0< _t, &_t::template foo<5> >(); 
} 

y llame al f1<some_class>() y luego NO obtengo un error de compilación en &_t::template foo<5>. Esto sugiere que el problema solo surge al obtener la dirección de una función de miembro con plantilla de un tipo que a su vez es un parámetro de plantilla en un contexto SFINAE. Argh!

+0

C++ han cancelado en 5 años: D Eche un vistazo a http://stackoverflow.com/questions/36780867/i-neet-to-know-at-compilation-time-if-class-a-has- a-function-member-called-b/36780868 # 36780868 –

Respuesta

1

Hay algo similar ya implementado en Boost.MPL, se llama "BOOST_MPL_HAS_XXX_TRAIT_DEF".Ver:

http://www.boost.org/doc/libs/1_41_0/libs/mpl/doc/refmanual/has-xxx-trait-def.html

Es capaz de detectar si la clase tienen un nombre dada tipo.

Además, para su caso específico, en lugar de pasar el puntero de función como un parámetro (void (_t :: *)()), trata de usarlo en el cuerpo de su método, es decir, algo así como:

template < typename _t > 
static big test(sfinae<_t>) 
{ 
    &_t::foo<_n>; 
} 
+0

Gracias por el enlace de Boost MPL. Aunque no parecen detectar funciones, su método es similar y puede solucionar el problema que tengo. Ahora tengo que separar sus macros :) Creo que el puntero a la función debe obtenerse como parte de la firma del método para que el compilador lo use para la resolución de sobrecarga. Cuando lo muevo al método, el compilador siempre elige la sobrecarga "grande" de la prueba para que "valor" sea verdadero incluso cuando la función no existe. – Aozine

Cuestiones relacionadas