7

I know this was not possible in C++03, but I'm hoping there is some new voodoo to allow me to do this. See below:

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)
}

Is there any way to do this in C++11? Will decltype help?

** UPDATE: Using Vlad's example I Was thinking something like this would work (caveat: I have not compiled as I am building the compiler with decltype support now)

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>();  
}

Would this work?

Jaime
  • 1,182
  • 2
  • 12
  • 29
  • 2
    Given that you are explicitly specifying it, I doubt there is a way. It should work even in C++03 if it was `AddMatch(&TestType::i)` instead. – K-ballo May 17 '12 at 19:57
  • 2
    What do you need to do with the pointer-to-member? It may be that there's a better solution than using a pointer-to-member as a non-type template parameter. – bames53 May 17 '12 at 20:17

4 Answers4

3

What you are trying to do cannot be done, that is, you cannot use a member pointer as a constant expression unless you have a type. That is, the type of a non-type template argument must be provided, or in other words, there is no type inference for template arguments.

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • Actually I think Vlad is on the right track. I'm gonna play with his concept a bit and see if it fits my what I am doing... – Jaime May 17 '12 at 20:14
  • @Jaime: Are you sure? In Vlad's code, the argument to the template is only the *type* of the member function, but the member function is not passed as a template argument. If you want to have the pointer-to-member as a compile time constant, you will need to call it like: `b.AddMatch();`, which is even more cumbersome unless you hide it behind a macro (which I don't quite like as an idea). If you don't mind having the pointer-to-member as a runtime value, then you can pass it to the function and the compiler will deduce it, but that changes the problem. – David Rodríguez - dribeas May 17 '12 at 20:20
  • ... which is what kballo pointed out in the comment to your question. – David Rodríguez - dribeas May 17 '12 at 20:22
2

How about this ( as a kicker it works in 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 );
}
Ylisar
  • 4,293
  • 21
  • 27
  • This is what the two comments suggest: changing the template argument to be a function argument. It is fine if you don't need/want the pointer to member to be a template argument... – David Rodríguez - dribeas May 17 '12 at 20:26
  • Ah, yeah that's true. I don't quite see any benefits of the interface the poster wants to use however compared to just passing it as an argument for type deduction. – Ylisar May 17 '12 at 20:32
  • Well, the generated code might be slightly faster in the case of a template argument, as the compiler can inject the exact call rather than dispatch through the pointer, but as you say, in most scenarios it will not make a difference. +1 – David Rodríguez - dribeas May 17 '12 at 20:37
2

You could make "T" provide this information.

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> 
{ }

Then it becomes easy

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>();
}

Alternatively you can use friend function definitions

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> 
{ }

Then you can write

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);
}
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
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)>();
}
  • Yea, that would work I bet, but I would like to prevent my users from having to specify decltype. I guess I could force FT to be a member field with traits and create a constant param that grabs decltype(&TestType:i)... – Jaime May 17 '12 at 20:00
  • This doesn't work. The template parameter from the question is a non-type template parameter. The function AddMatch is parameterized on a pointer to member, not on the type of a pointer to member. – bames53 May 17 '12 at 20:15
  • This does not solve the original problem, it just replaces the first argument `int` with a more cumbersome `decltype(&TestType::i)` (and forgets to provide the actual pointer to member as the second template argument...) Where in the original code `TestType::i` is a known value, in this answer the actual value disappears (only the type is there) – David Rodríguez - dribeas May 17 '12 at 20:21