I feel a slight misconception in your understanding of the case that you encountered.
First of all, this is a function template:
struct A { template <typename... Args> void f(Args... args) { } };
And this is not a function template:
template <typename... Args> struct A { void f(Args... args) { } };
In the first definition (with the function template), the deduction of the argument type occurs. In the latter case, there is no type subtraction.
You are not using a function template. You are using a non-template member function from a class template, and for this particular member function its signature is fixed.
Defining your trap class as shown below:
template <typename T, T t> struct trap; template <typename R, typename... Args, R(Base::*t)(Args...)> struct trap<R(Base::*)(Args...), t> { static R call(Args... args); };
and referring to its member function as shown below:
&trap<decltype(&Base::target), &Base::target>::call;
you get a pointer to a static non-template call function with a fixed signature, identical to the signature of the target function.
Now this call function serves as an intermediate call. You will call function, and this function will call the target member function, passing its own arguments to initialize the target parameters, say:
template <typename R, typename... Args, R(Base::*t)(Args...)> struct trap<R(Base::*)(Args...), t> { static R call(Args... args) { return (get_base()->*t)(args...); } };
Assume that the target function used to instantiate the template for the trap class is defined as follows:
struct Base { int target(Noisy& a, Noisy b); };
By creating an instance of the trap class, you get the following call function:
Fortunately, a is passed by reference, it is simply redirected and attached to the same type of links in the target parameter. Unfortunately, this does not apply to object b - regardless of whether the Noisy class is movable or not, you make several instances of instance b , since this one is passed by value:
Demo 1
This is somewhat inefficient: you could save at least one constructor invocation by turning it into a move constructor invocation if you could turn an instance of b into an x โโvalue:
static int call(Noisy& a, Noisy b) { return get_base()->target(a, std::move(b));
Now it will call the move constructor instead of the second parameter.
So far, everything is fine, but it was done manually ( std::move added, knowing that it is safe to apply the semantics of movement). Now the question is, how can you use the same functionality when working with the parameter package ?:
return get_base()->target(std::move(args)...);
You cannot apply the call to std::move to each argument in a parameter package. This will probably lead to compiler errors if they apply equally to all arguments.
Demo 2
Fortunately, although Args... not a referral link, the helper function std::forward can be used instead. That is, depending on whether the type <T> is in std::forward<T> (lvalue reference or non-lvalue-reference), std::forward will behave differently:
for lvalue references (for example, if T is Noisy& ): the category of expression values โโremains an lvalue (i.e. Noisy& ).
for non-lvalue references (for example, if T is Noisy&& or plain Noisy ): the value category of the expression becomes xvalue (i.e. Noisy&& ).
Having said that, defining the target function as shown below:
static R call(Args... args) { return (get_base()->*t)(std::forward<Args>(args)...); }
in the end it will turn out:
static int call(Noisy& a, Noisy b) {
turning the category of expression values โโfrom b into the value of x b , which is equal to Noisy&& . This allows the compiler to select a move constructor to initialize the second parameter of the target function, leaving a intact.
DEMO 3 (compare the output with DEMO 1)
In principle, std::forward serves for this. Typically, std::forward used with a referral link, where T contains the type deduced according to the type inference rules for referring links. Please note that you always need to explicitly pass the <T> , since it will use different behavior depending on this type (independent of the category of values โโof its argument). Without an explicit template argument of type <T> , std::forward always displays lvalue references for arguments passed through their names (for example, when expanding the parameter package).
Now you would like to additionally convert some arguments from one type to another, sending all the others. If you are not interested in the trick with the arguments std::forward ing from the parameter package, and this is normal, if you always call the copy constructor, then your version is fine :
template <typename T> // transparent function T&& process(T&& t) { return std::forward<T>(t); } Bar process(Foo x) { // overload for specific type of arguments return Bar{x}; } //... get_base()->target(process(args)...);
Demo 4
However, if you want to avoid copying this Noisy argument into the demo, you need to somehow combine the std::forward call with the process call, and go through Args so that std::forward can apply the correct behavior (turning into x values โโor nothing not to do). I just gave you a simple example of how this can be implemented:
template <typename T, typename U> T&& process(U&& u) { return std::forward<T>(std::forward<U>(u)); } template <typename T> Bar process(Foo x) { return Bar{x}; }
But this is only one option. It can be simplified, overwritten, or reordered, so std::forward is called before calling the process function (your version):
get_base()->target(process(std::forward<Args>(args))...);
DEMO 5 (compare the output with DEMO 4)
And this will work well (i.e. with your version). So the fact is that the additional std::forward just optimizes your code a little, and the idea presented was only one of the possible implementations of this functionality (as you can see, this leads to the same effect).