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.

6
  • 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 ? Commented Oct 18, 2015 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. :-) Commented Oct 18, 2015 at 17:58
  • Then why do you need Member as a non-type template parameter? Isn't a signature enough? Is this what you need? Commented Oct 18, 2015 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? Commented Oct 18, 2015 at 18:58
  • I'm only trying to clarify what is your goal Commented Oct 18, 2015 at 19:04

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

6 Comments

In your first snippet, both R and ...P are not deducible: 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.
The right call is something like MyClass{}.bar<woof, double, int, char>(&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... :-)
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<class C, class R, class... P> struct My { template<R(C::*M)(P...)> struct Class {}; }; My<woof, double, int, char>::Class<&woof::foo>{}
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.
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<double(woof::*)(int, char), &woof::foo>(&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?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.