2012-05-17 18 views
6

Sé que esto no fue posible en C++ 03, pero espero que haya algún vudú nuevo que me permita hacer esto. Vea debajo:¿Hay alguna forma en C++ 11 para obtener el tipo de puntero de miembro dentro de una plantilla?

template <class T> 
struct Binder 
{ 
    template<typename FT, FT T::*PtrTomember> 
    void AddMatch(); 
}; 
struct TestType 
{ 
    int i; 
}; 
int main(int argc, char** argv) 
{ 
    Binder<TestType> b; 
    b.AddMatch<int,&TestType::i>(); //I have to do this now 
    b.AddMatch<&TestType::i>(); //I'd like to be able to do this (i.e. infer field type) 
} 

¿Hay alguna manera de hacer esto en C++ 11? ¿Ayudará decltype?

** ACTUALIZACIÓN: Usando el ejemplo de Vlad estaba pensando algo como esto iba a funcionar (advertencia: no he compilado como estoy construyendo el compilador con el apoyo decltype ahora)

template <class T> 
struct Binder 
{ 
    template<typename MP, FT ft = decltype(MP)> 
    void AddMatch() 
    { 
     //static_assert to make sure MP is a member pointer of T 
    } 
}; 
struct TestType 
{ 
    int i; 
}; 
int main() 
{ 
    Binder<TestType> b; 
    b.AddMatch<&TestType::i>(); 
} 

Que este trabajo?

+2

Dado que lo está especificando, dudo que haya una manera. Debería funcionar incluso en C++ 03 si fuera 'AddMatch (& TestType :: i) 'en su lugar. –

+2

¿Qué necesitas hacer con el puntero a miembro? Puede ser que haya una mejor solución que usar un puntero a miembro como un parámetro de plantilla sin tipo. – bames53

Respuesta

3

Lo que estamos tratando de hacer no se puede hacer, es decir, no se puede utilizar un puntero miembro como una expresión constante a menos que tenga un tipo. Es decir, se debe proporcionar el tipo de argumento de plantilla sin tipo, o en otras palabras, no hay inferencia de tipo para los argumentos de la plantilla.

+0

En realidad, creo que Vlad está en el camino correcto. Voy a jugar un poco con su concepto y ver si encaja con lo que estoy haciendo ... – Jaime

+0

@Jaime: ¿Estás seguro? En el código de Vlad, el argumento para la plantilla es solo el * tipo * de la función miembro, pero la función miembro no se pasa como un argumento de plantilla. Si desea tener el puntero a miembro como una constante de tiempo de compilación, tendrá que llamarlo así: 'b.AddMatch ();', que es incluso más engorroso a menos que lo ocultes detrás de una macro (que no me gusta como idea). Si no le importa tener el puntero a miembro como valor de tiempo de ejecución, puede pasarlo a la función y el compilador lo deducirá, pero eso cambia el problema. –

+0

...que es lo que Kballo señaló en el comentario de tu pregunta. –

0
template <class T> 
struct Binder 
{ 
    template<typename FT> 
    void AddMatch(); 
}; 

struct TestType 
{ 
    int i; 
}; 

int main() 
{ 
    Binder<TestType> b; 
    b.AddMatch<decltype(&TestType::i)>(); 
} 
+0

Sí, eso apuesto, pero me gustaría evitar que mis usuarios tengan que especificar decltype. Supongo que podría forzar a FT a ser un campo miembro con rasgos y crear un param constante que capture decltype (& TestType: i) ... – Jaime

+0

Esto no funciona. El parámetro de plantilla de la pregunta es un parámetro de plantilla sin tipo. La función AddMatch está parametrizada en un puntero al miembro, no en el tipo de un puntero al miembro. – bames53

+0

Esto no resuelve el problema original, simplemente reemplaza el primer argumento 'int' con un' decltype 'más engorroso (& TestType :: i)' (y olvida proporcionar el puntero real al miembro como el segundo argumento de plantilla ...) Donde en el código original 'TestType :: i' es un valor conocido, en esta respuesta el valor real desaparece (solo el tipo está allí) –

2

¿Qué hay de esto (como un kicker funciona en C++ 03):

#include <iostream> 
#include <typeinfo> 

template< typename T > struct ExtractMemberTypeHelper; 
template< typename R, typename T > 
struct ExtractMemberTypeHelper< R(T::*) > 
{ 
    typedef R Type; 
    typedef T ParentType; 
}; 

template< typename T > 
struct ExtractMemberType : public ExtractMemberTypeHelper<T> {}; 

struct foo 
{ 
    int bar; 
    template< typename T > 
    void func(const T& a_Arg) 
    { 
     std::cout << typeid(typename ExtractMemberType<T>::Type).name() << " " << typeid(typename ExtractMemberType<T>::ParentType).name() << std::endl; 
    } 
}; 

int main() 
{ 
    foo inst; 
    inst.func(&foo::bar); 
} 
+0

Esto es lo que sugieren los dos comentarios: cambiar el argumento de la plantilla para que sea un argumento de función. Está bien si no necesita/desea que el puntero al miembro sea un argumento de plantilla ... –

+0

Ah, sí, eso es verdad. No veo absolutamente ningún beneficio de la interfaz que el póster quiere usar, sin embargo, en comparación con solo pasarlo como argumento para la deducción de tipo. – Ylisar

+0

Bueno, el código generado podría ser ligeramente más rápido en el caso de un argumento de plantilla, ya que el compilador puede inyectar la llamada exacta en lugar de enviarla a través del puntero, pero como dices, en la mayoría de los escenarios no hará la diferencia. +1 –

2

usted podría hacer "T" proporcionar esta información.

template <class ...T> 
struct BoundTypes { }; 

template <class U, class T> 
struct BinderDecls { 
    void AddMatch(); 
}; 

template <class U, class T, class ...Ts> 
struct BinderDecls<U, BoundTypes<T, Ts...>> 
    :BinderDecls<U, BoundTypes<Ts...>> 
{ 
    using BinderDecls<U, BoundTypes<Ts...>>::AddMatch; 

    template<T U::*PtrTomember> 
    void AddMatch(); 
}; 

template <class T> 
struct Binder : BinderDecls<T, typename T::bound_types> 
{ } 

entonces se hace fácil

struct TestType { 
    typedef BoundTypes<int, float> bound_types; 

    int i; 
    float j; 
}; 

int main(int argc, char** argv) 
{ 
    Binder<TestType> b; 
    b.AddMatch<&TestType::i>(); 
    b.AddMatch<&TestType::j>(); 
} 

Alternativamente, puede utilizar la función amigo definiciones

template <class ...T> 
struct BoundTypes { }; 

template <class U, class T> 
struct BinderDecls { 
    template<T U::*ptr> 
    friend void addMatch(BinderDecl &u) { 
    // ... 
    } 
}; 

template <class U, class ...Ts> 
struct BinderDecls<U, BoundTypes<Ts...>> : BinderDecls<U, Ts>... 
{ }; 

template<typename = void> 
void addMatch() = delete; 

template <class T> 
struct Binder : BinderDecls<T, typename T::bound_types> 
{ } 

entonces usted puede escribir

struct TestType { 
    typedef BoundTypes<int, float> bound_types; 

    int i; 
    float j; 
}; 

int main(int argc, char** argv) 
{ 
    Binder<TestType> b; 
    addMatch<&TestType::i>(b); 
    addMatch<&TestType::j>(b); 
} 
Cuestiones relacionadas