3

I'm in trouble for the way I should reference arguments in a template (honestly, I strongly suspect that it's not possible to achieve what I'd like to do).

It follows an example of what I'd like to do (of course, this is not syntactically legal, the aim is to give the idea of which is the target):

template<class C, Ret(C::*Member)(Params...), typename Ret, typename... Params>
class MyClass { }

In other terms, I'd like to reference a member of a class, by specifying at the same time also which is the returned value and the parameters of that method.

Unfortunately, the only way I see to do that is something like the following one (well, it depends indeed on where those typenames are required, anyway it may be a meaningful example):

template<typename Ret, typename... Params>
class MyClass {
public:
    template<class C, Ret(C::*Member)(Params...)>
    MyClass(C *c) { /* do something else and give sense to this class */ }
}

Besides the one above, that is to break the interlacing by introducing a templated constructor, there exists another valid approach to obtain the same result with the sole class template signature?

I know (pretty simple) how to achieve that in case of not variadic template (as an example, move Ret before Member), but the variadic one (Params) has to lay at the end of the template list and I cannot refer it in any way.

skypjack
  • 49,335
  • 19
  • 95
  • 187
  • why do you want to explicitly pass all types, instead of using a `typename T, T t` approach, and then `decltype(&C::member), &C::member` ? – Piotr Skotnicki Oct 18 '15 at 17:44
  • I'd like not to bind to a specific member, but only to a contract, thus I have to explicitly define which is the target member for that template. Following your approach, I would be forced to define a member with a well known name and prototype in my classes, haven't I? Moreover, it's an alternative solution to work around the problem, not really a response to my question. :-) – skypjack Oct 18 '15 at 17:58
  • Then why do you need `Member` as a non-type template parameter? Isn't a signature enough? Is [this](http://coliru.stacked-crooked.com/a/b0d3947a79be7738) what you need? – Piotr Skotnicki Oct 18 '15 at 18:50
  • You did it by relying on partial specialization. Could be a solution, but it's an alternative one like the one I found for myself, good enough, but it doesn't reply to my question. Are you trying to say me that it's not possible to refer variadic arguments from within the template signature, like I can do with all the other arguments? – skypjack Oct 18 '15 at 18:58
  • I'm only trying to clarify what is your goal – Piotr Skotnicki Oct 18 '15 at 19:04
  • Absolutely, thank you indeed!! :-) – skypjack Oct 18 '15 at 19:06

1 Answers1

2

According with this question, a viable solution could be to rely on deduction forced by a default value.

As an example, the following code should be valid:

class MyClass {
public:
    template <class C, typename R, typename... P, R(C::*M)(P...) = &C::foo>
    void bar(C *c) { }
};

I cite part of the linked question (a citation that is a citation for itself, I'm in a loop):

A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list of the function template or has a default argument.

Because of that, the following code should not be allowed instead, even if it compiles with GCC:

class MyClass {
public:
    template <class C, typename R, typename... P, R(C::*M)(P...)>
    void bar(C *c) { }
};

Well, quite tricky, not so flexible a solution and honestly I ended up far ago with a bit of refactoring, but for the sake of clarity I've decided to add an answer and close the question with a snippet that compiles.

Community
  • 1
  • 1
skypjack
  • 49,335
  • 19
  • 95
  • 187
  • In your first snippet, both `R` and `...P` are not deducible: http://melpon.org/wandbox/permlink/lUlLmimPYbyr22C6 For `...P`, this means it is "deduced" to be an empty parameter pack, and hence `&C::foo` won't be compatible with the type of `M` unless `M` has no parameters. – dyp Jan 23 '16 at 10:52
  • The right call is something like `MyClass{}.bar(&w);`. There is no way to deduce `R` or `P...` in the example, but the issue was about the last type after the parameter pack, and it must be deducible or defaulted (as it is in the example). So, well, it's true, you are right, but it is not what I was discussing... :-) – skypjack Jan 23 '16 at 10:58
  • Ah I see. I didn't entirely understand from your question what you wanted to do. An alternative to defaulting the non-type parameter is to nest this is another template: `template struct My { template struct Class {}; }; My::Class<&woof::foo>{}` – dyp Jan 23 '16 at 11:02
  • I've removed the *accepted* flag from my answer. Feel free to add more details to your comment and propose another answer, I would be glad to look over it and accept it if it actually solves the problem in a more elegant and flexible way. – skypjack Jan 23 '16 at 11:05
  • The more I think about it, the less I understand what your aim is xD With Piotr's approach mentioned in the comments to your question, the call would be `MyClass{}.bar(&w);` so the member `foo` is mentioned at the caller's site not the callee's site. I don't quite understand your concerns about this approach mentioned in your answers to Piotr's suggestions. Shall the caller not be able to provide their member's name? – dyp Jan 23 '16 at 11:11
  • Piotr's solution is interesting, but I need to name the member method of `C` to be able to reference it. So, while `template class MyClass` compiles fine, try using `template class MyClass` instead and you'll get and error. Unfortunately, in the first solution you are defining a type (that is `Ret(C::*)(Params...)`), but what I need is an actual pointer to a member method. – skypjack Jan 23 '16 at 11:17