Home > other >  Class member names as non-type template parameter
Class member names as non-type template parameter

Time:07-26

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?

CodePudding user response:

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.

  • Related