cheshirekow
cheshirekow

Reputation: 4907

Compact way to deduce extra template parameter types for pointer-to-member template arguments

Note: this question is largely academic.

We can use a pointer-to-member as a template parameter in a class template, but if the "dependant" types are not known we must declare all of them as template parameters. For examples:

// Our template operates with a "pointer to member", but it's generic for
// both the type of the member value itself, as well as the owner type of
// which the value is a member.
template <class T, class V, V T::*memptr>
struct Foo {};

In this case we must include T, V as template parameters if the template functionality is generic over these types. Usage of this template seems rather verbose, however:

struct Bar {int a;};
struct Baz {double a;};

int main(int argc, char** argv) {
  // The first two parameters "Bar, int" are kinda redundant
  Foo<Bar, int, &Bar::a> foo1;
  // Same here with Baz, double
  Foo<Baz, double, &Baz::a> foo2;
}

In particular the template parameter &Bar::a is a non-type parameter whose type is int &Bar::*. Thus, it seems that T=Bar, V=int is quite obvious.

Is there a syntax, metaprogram, or construct that can be exploited so that we can instantiate the template with only memptr=&Bar::a and allow T=Bar, V=int to be deduced/inferred?

Ideally we would instantiate the template with simply:

Foo<&Bar::a> foo1;
Foo<&Baz::a> foo2;

But I can't seem to figure out a way to achieve this. I have a solution that utilizes a preprocessor macro:

// This is an ugly metaprogram and macro to shorten-up usages of the template
template <class T, class V>
struct Step2 {
  template <V T::*memptr>
  struct Step3 {
    typedef Foo<T, V, memptr> Type;
  };
};

template <class T, class V>
Step2<T, V> step1(V T::*memptr);

#define FOO(T, MEMBER) decltype(step1(&T::MEMBER))::Step3<&T::MEMBER>::Type

Which can be used as:

int main(int argc, char** argv) {
  // It seems that thre should be a more compact way to instantiate this
  // template. Indeed the macro does this, but macros are tough because they're
  // global names and it can make compiler errors really tough to comprehend.
  FOO(Bar, a) foo1;
  FOO(Baz, a) foo2;
}

But this does not seem like a problem that needs preprocessor macros as a solution.

Upvotes: 0

Views: 40

Answers (1)

parktomatomi
parktomatomi

Reputation: 4079

Similar question: Deducing pointer-to-member template arguments

The answer from there is to lean on C++17 auto magic:

template <auto p>
struct Foo {};

Upvotes: 1

Related Questions