Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where is ... (ellipsis) supposed to be placed in a C++ parameter pack expansion? Inside or outside of the parenthesis for std::forward? [duplicate]

I am new to C++ parameter pack expansion, and I have been confused about the position of ... (ellipsis) for a long time, eg:

template <typename... Args> 
void func(Args&&... args) {
    // ...
    std::forward<Args>(args)...;
    // ...
}

Why isn't there std::forward<Args>(args...)? What is the rule I should follow?

like image 354
Bird Avatar asked Sep 03 '25 09:09

Bird


2 Answers

Check out https://en.cppreference.com/w/cpp/language/parameter_pack, particularly the "Pack expansion" section.

Basically, the "..." in the pack expansion is almost like a macro: it creates more source code, in a way. Whatever is before the "..." just gets repeated for each argument (and the pack-name replaced with said argument), so something like func(args...) would be expanded as "func(a, b, c)", while func(args)... would turn into "func(a), func(b), func(c)".

std::forward just takes one argument and produces one forwarded item: func(std::forward(args)...) will turn into a list of individual forwards: func(std::forward(a), std::forward(b), std::forward(c)). So, it takes a list of arguments, and produces a list of forwarded arguments

like image 121
Christian Stieber Avatar answered Sep 04 '25 21:09

Christian Stieber


Grammatically, std::forward<Args>(args)...; doesn't make sense. You can only use a pack expansion within parentheses or braces.

If you were to call a function, say foo, then you would write:

foo(std::forward<Args>(args)...)

This would be a postfix-expression foo(/* ... */), which contains an expression-list, which is an initializer-list with a single initializer-clause std::forward<Args>(args) and a ....

In other words, the ... isn't part of the expression; it's part of the list of expressions. Therefore, ... is the "outermost" thing, as it has lower precedence than any expression. *args... and args()... would apply * and () to each argument in args respectively. You can think of X... as an instruction that says:

Do X for each element in the pack.

std::forward<Args>(args...) is wrong because the ... here applies to the initializer-list of arguments when calling std::forward, but std::forward is not a variadic function template. Instead of forwarding each argument, it attempts to call std::forward with the whole pack passed in.

See also How would one call std::forward on all arguments in a variadic function?

like image 24
Jan Schultke Avatar answered Sep 04 '25 22:09

Jan Schultke



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!