Home > Back-end >  Parameter pack extraction
Parameter pack extraction

Time:10-03

I have a function2, which can be called with or without a second argument == char. If so, I want to modify that char-Argument.

Given

void function1_caller(int x) { 
    char ws=7; 
    function2_modifyArg(x, ws); 
}

This works:

template <typename ... WS> 
void function2_modifyArg(int x, WS ... ws) { 
    function3_end(x, ws ...);
}

simply put in parenthesis, doesn't work already:

template <typename ... WS> 
void function2_modifyArg(int x, WS ... ws) { 
    function3_end(x, (ws ... )); 
}

This results in the following error:

Syntax error: unexpected token '...', expected declaration

In fact here I'd like to modify the argument, but of course I get the same

Syntax error: unexpected token '...', expected declaration
template <typename ... WS> 
void function2_modifyArg(int x, WS ... ws) { 
    function3_end(x, (ws ... / 3));
}

template <typename ... WS> 
void function3_end(int x, WS ... ws) {};

CodePudding user response:

ws... expands to a comma-separated list of the values in ws, but only in specific contexts, not anywhere such a list could produce valid syntax. One such context is as a series of function arguments as in function3_end(x, ws ...).

Both (ws...) and (ws... / 3) aren't allowed. If this were simply a note to the compiler to "Hey, expand this out and then try to compile it", both would actually compile for non-empty packs, but the first would be a series of comma operators and the second would have the last value divided by 3. Neither is particularly as helpful as a compiler error. The first can even be done explicitly using a fold expression, e.g., (..., ws).

What you can do is pick one of those valid contexts and apply a pattern to each element of the pack:

function3_end(x, (ws/3)...);

This copies the pattern while expanding and works fairly intuitively once you see some expamples. The ellipsis applies to the "group" of syntax to its left, which is (ws/3) in this case, leading to an expansion of (ws₀/3), (ws₁/3), (ws₂/3), …. In the single-element case, this is equivalent to (ws₀/3), and with no elements, the entire pattern is "copied" 0 times, which is exactly what you want. Parentheses are not mandatory for this simple pattern, but I would recommend them in general to make the expansion scoping perfectly clear.


Now all that said, I would rather avoid the template and use an overload:

void function3_end(int x);
void function3_end(int x, char ws);

Detection is easy—one body is for the second argument and one is for without. This also signals that the second argument is a char, not any type that may or may not have some extra constraint added on top to make it a char in disguise. The variadic template doesn't even signal that this function can't be called with more than 2 arguments. In addition, these overloads can be split from their declaration and placed into a separately compiled source file, as well as preventing extra template instantiations. Finally, if this would violate DRY, you can factor the common parts into separate functions that are used within both overloads.

CodePudding user response:

You don't need the parenthesis you just need to put the operation you want to do before the pack expansion:

template <typename ... WS> 
void function2_modifyArg(int x, WS ... ws) { 
    function3_end(x, ws / 3 ...);
}
  • Related