Pointer to class member as a template parameter

2019-01-18 10:37发布

问题:

I want to use a pointer to a class member as a template parameter as in:

template <class Class, class Result, Result Class::*Member>
struct MyStruct {
    // ...
};

Using this struct like MyStruct<SomeClass, SomeResult, &SomeClass::value> variable works just fine, but I don't like that I have to specify SomeClass and SomeResult.

I would like to use MyStruct<&SomeClass::value> variable if that is possible, but without losing the ability to pass any class and have any result type.

I tried the following, but the syntax is illegal:

template <class Class, class Result>
template <Result Class::*Member>
struct MyStruct {
    // ...
};

error: too many template-parameter-lists

I tried using a helper function (that does actually work in Clang but is refused by GCC):

template <class Class, class Result>
static constexpr auto makeMyStruct(Result Class::*member) ->
MyStruct<Class, Result, member> {
    // ...
}

error: use of parameter `member' outside function body
error: template argument 3 is invalid

Is it possible to have a simple MyStruct<&SomeClass::value>, and if so, how?

Related question that did not solve my question:

  • Pointer to class member as template parameter
  • C++0x error with constexpr and returning template function

回答1:

This could be a solution in C++11:

You can define the following generic type traits:

template<class T>
struct remove_member_pointer {
  typedef T type;
};

template<class Parent, class T> 
struct remove_member_pointer<T Parent::*> {
  typedef T type;
};

template<class T>
struct baseof_member_pointer {
  typedef T type;
};

template<class Parent, class T>
struct baseof_member_pointer<T Parent::*> {
  typedef Parent type;
};

Now you can define an additional, 4-line wrapper macro for every struct:

template<class Class, class Result, Result Class::*Member>
struct _MyStruct {
  // ...
};

#define MyStruct(MemberPtr) \
  _MyStruct<baseof_member_pointer<decltype(MemberPtr)>::type, \
            remove_member_pointer<decltype(MemberPtr)>::type, \
            MemberPtr>

... and use it in the following way:

MyStruct(&SomeClass::value)  myStruct; // <-- object of type MyStruct<&SomeClass:value>

I use this as an intermediate solution, until we switch to C++17.



回答2:

An answer to my question was proposed in this paper for the next upcoming C++ standard:

  • https://isocpp.org/files/papers/n3601.html

This syntax was proposed:

template<using typename T, T t>
struct some_struct { /* ... */ };

some_struct<&A::f> x;

The need for a new syntactical construct indicates that you cannot do that by now.

I hope n3601 will be accepted. :-)



回答3:

In c++17, with the addition of auto in template arguments (P0127), I think you can now do:

template<auto value>
struct MyStruct {};

template<typename Class, typename Result, Result Class::* value>
struct MyStruct<value> {
    // add members using Class, Result, and value here
    using containing_type = Class;
};

typename MyStruct<&Something::theotherthing>::containing_type x = Something();


回答4:

Make your result class a child of your template class. assuming the pointer member is an object of your result class in public or whatever, you can access any objects by doing something like this

template <stuff for this class> :: public result
{
    blah
}