32

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:

Community
  • 1
  • 1
Kijewski
  • 25,517
  • 12
  • 101
  • 143
  • Dup? http://stackoverflow.com/questions/5628121/is-it-possible-to-emulate-templateauto-x – GManNickG Mar 01 '13 at 01:38
  • @GManNickG I'm not sure if my question boils down to that other question. Mine is far more narrow, and I'd be not too happy using a macro. – Kijewski Mar 01 '13 at 01:43
  • 1
    The term is *pointer-to-member*, not *reference* to member. References and pointers are quite different in the language (well, not *that* different, but still not the same) – David Rodríguez - dribeas Mar 01 '13 at 04:55
  • The issue with your helper function is the use of the runtime value `member` as a template argument, which must be known at compile-time to instantiate the template. If you were to change MyStruct to take the `Result Class::*member` as a constructor parameter instead of template parameter then it works fine. – boycy May 08 '13 at 09:56
  • @boycy, you are missing the point … http://isocpp.org/files/papers/n3601.html proposes a solution to the problem. – Kijewski May 08 '13 at 17:27
  • @Kay - I understood what you were trying to achieve & how N3601 provides you a solution. I wished to add for the benefit of anyone else visiting (and you, in case you were unaware) why the helper function wouldn't compile. I'm confused by Clang accepting it though... – boycy May 09 '13 at 08:33
  • @boycy I did not mention it properly, but the argument to the ctor should be known at compile time. As in `auto x = makeMyStruct(&SomeStruct::some_member);`. N3601 comes in handy in this case. – Kijewski May 09 '13 at 22:12

4 Answers4

18

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();
Eric
  • 95,302
  • 53
  • 242
  • 374
11

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

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. :-)

Kijewski
  • 25,517
  • 12
  • 101
  • 143
8

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.

ManuelAtWork
  • 2,198
  • 29
  • 34
-8

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
}
Shawn
  • 667
  • 8
  • 19