4

I would like to write a function that apply a process to member of a class. The following code is working:

class AA
{
public:
    AA(){};
    ~AA(){};
    std::string type="AA";
};

class BB
{
public:
    BB(){};
    ~BB(){};
    template <typename T, typename TT>
    void test(T& a, TT(T::*memberPtr))
    {
        std::cout<<"test: "<<(a.*memberPtr)<<std::endl;
    }
    std::string type="BB";
};

int main()
{
    AA a;
    BB b;
    b.test(a, &AA::type);
}

But I know everything at compile-time so I am wondering if it is possible to write something equivalent but only with templates? So I could write something like:

b.test<&AA::type>(a);

that calls inside test(a):

std::cout<<"test: "<< (a.*MEMBER) <<std::endl; // MEMBER is given in template

or something like that.

Barry
  • 286,269
  • 29
  • 621
  • 977
StormRider
  • 402
  • 1
  • 5
  • 13
  • You could pass a pointer to a getter method instead of the member variable. – Robin Krahl Aug 20 '15 at 13:07
  • The `memberPtr` variable in your `test` function already does what you want - member function pointers are runtime values. – Griwes Aug 20 '15 at 13:08
  • I realize that I may have misunderstood what my pointer memberPtr really is... is it determined at compile time or at run time? Is there any cost for using (a.*MEMBER) compare to just writting 'a.type' ? – StormRider Aug 20 '15 at 13:59
  • @user52730 I expect there to be the cost of dereferencing a pointer but that's probably insignificant and this is premature optimization. Unless you have measured your code and confirmed this is particularly problem I would focus on making things clear. – Chris Drew Aug 21 '15 at 08:31

3 Answers3

9

You can't do just test<&AA::type>, since you'd need to also tell the function template what type of pointer-to-member you're expecting. The typical pattern is:

template <class M, M member, class T> // deduced go last
void test(T& a) {
    cout << (a.*member);
}

With usage:

test<decltype(&AA::type), &AA::type>

I believe there's currently a proposal to reduce the verbosity there, but until then, it's not the worst thing in the world, and you could always:

#define TYPE_AND_VAL(foo) decltype(foo), foo
test<TYPE_AND_VAL(&AA::type)>

That proposal I mentioned is in C++17, and will allow you to do:

template <auto member, class T>
void test(T& a) {
    cout << (a.*member);
}

test<&AA::type>(a);
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thank you for your answer, it is closer to what I want but I am wondering: you still use pointer so I guess it is determined at runtime? (I am not really aware of function pointers) As I exactly know which member I will point to at compile time, I guessed it was possible to define everything at compile time. – StormRider Aug 20 '15 at 14:04
  • @user52730 This *is* at compile-time. Template arguments must be known at compile time. – Barry Aug 20 '15 at 14:06
  • Ok and can you confirm that my code was dealing with the pointer at runtime? Function pointers are still quite obscur to me. – StormRider Aug 20 '15 at 14:09
  • @user52730 Yes, you're passing an argument - that's a good sign that it's at runtime. There are no function pointers anywhere in this code. – Barry Aug 20 '15 at 14:11
2

This is related to SergeyA's answer but a compromise would be to pass your class and a lambda. The compiler is able to inline the lambda so you will probably not pay for it at run-time:

struct BB
{
public:
    template <typename T, typename F>
    void test(T& a, F f)
    {
        std::cout<<"test: "<< f(a) <<std::endl;
    }
    std::string type="BB";
};

int main()
{
    AA a;
    BB b;
    b.test(a, [](AA& a){return a.type;});
}

You may (or may not) find this clearer than using pointer-to-class-member.

Community
  • 1
  • 1
Chris Drew
  • 14,926
  • 3
  • 34
  • 54
  • That's interesting thanks! I look for something more readable than lambda though. – StormRider Aug 26 '15 at 14:55
  • @user52730 Personally, I find lambda's more readable than pointer-to-member-function syntax but ok. You can use `auto` with lambdas if you want to give it a name. – Chris Drew Aug 26 '15 at 14:59
0

Just eliminate this somewhat strongly typed altogether. Instead of requesting user to provide you with a class and the member poiner, just accept templated functor and call it's operator(). This will be most flexible and least verbose.

SergeyA
  • 61,605
  • 5
  • 78
  • 137