1

I have a class which is generated from a tool with member variable names that vary. I would like to use a single templated class to access these members. I can do this in the following way by using a non-type member pointer:

template<typename MODULE, 
    int MODULE::*a> class Foo {
public:
    Foo() {
        MODULE mod;
        std::cout << mod.*a;
    }
};

struct Bar {
    int random_name01234{20};
};

int main(int argc, char** argv, char** env) {
    Foo<Bar, &Bar::random_name01234> foobar;
}

However, the generated class (Bar in this example) uses references to members I do not have access to. I cannot pass a reference as a non-type template parameter as described here - Reference as a non-type template argument:

template<typename MODULE, 
    int& MODULE::*a> class Foo {
public:
    Foo() {
        MODULE mod;
        std::cout << mod.*a;
    }
};

class Bar {
private:
    int random_name01234{20};
public:
    int& random_name01234_ref{random_name01234};
};

int main(int argc, char** argv, char** env) {
    Foo<Bar, &Bar::random_name01234_ref> foobar;
}

error: cannot create pointer to reference member ‘Bar::random_name01234_ref’

Is there another way I can approach this to pass the random member names to a templated function?

ttchuk
  • 13
  • 3
  • 1
    @virgesmith Nothing is being passed here that isn't known at compile-time. The template parameter is a red herring anyway. The issue is that you simply can't have a pointer-to-reference-member. – user17732522 Jul 25 '22 at 12:54
  • Related: [Reference as a non-type template argument](https://stackoverflow.com/questions/28662784/reference-as-a-non-type-template-argument) and [How to obtain pointer to reference member?](https://stackoverflow.com/questions/42119458/how-to-obtain-pointer-to-reference-member) – Jason Jul 25 '22 at 12:54
  • 1
    Instead of `mod.*a`, you write `std::invoke(a, mod)`. Use `auto` instead of member pointer as template parameter, and then you pass in lambdas returning reference members when you need them. – Passer By Jul 25 '22 at 13:00

1 Answers1

0

You cannot have a pointer to reference member (see eg How to obtain pointer to reference member? and Reference as a non-type template argument), but you can have a pointer to member function that returns the value by reference:

#include <iostream>


template<typename MODULE, 
    int& (MODULE::*a)()> class Foo {
public:
    Foo() {
        MODULE mod;
        std::cout << (mod.*a)();
    }
};

class Bar {
private:
    int random_name01234{20};
public:
    int& random_name01234_ref{random_name01234};
    int& get() { return random_name01234_ref;}
};

int main(int argc, char** argv, char** env) {
    Foo<Bar, &Bar::get> foobar;
}

If you cannot modify Bar then you just have to use a different callable that returns the reference. Anyhow I consider int (MODULE::*a)() as too non-generic. I'd rather let Foo accepts any callable, for example a lambda expression:

#include <iostream>


template<typename MODULE, typename F> class Foo {
public:
    Foo() {
        MODULE mod;
        std::cout << F{}(mod);
    }
};

class Bar {
private:
    int random_name01234{20};
public:
    int& random_name01234_ref{random_name01234};
};


int main(int argc, char** argv, char** env) {
    Foo<Bar,decltype([](Bar& b)->int&{ return b.random_name01234_ref;})> foobar;
}

Lambda expression in unevaluated context is only available since C++20. Before it works as well, just a little more verbose.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185