Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ parameter pack expansion

Tags:

c++

templates

The code below doesn't compile (see error below the code). Can you please explain why?

template <class F, class... Arg>
void for_each_argument(F f, Arg&&... arg)
{
   f(std::forward<Arg>(arg...));
}
int main()
{
   for_each_argument(
     [](const auto& a){std::cout<< a;}, "Aa", 3, 4);
   return 0;
}

Here is an error message:

7:4: error: expression contains unexpanded parameter pack 'Arg'

f(std::forward(arg...));

like image 349
i chik Avatar asked Mar 11 '26 16:03

i chik


2 Answers

You have several issues in your code. First of, your original line

f(std::forward<Arg>(arg...));

Is not correct syntax at all - you are expanding arg without properly expanding Arg in template. Now, you can fix at least that by

f(std::forward<Arg>(arg)...);

This would be better, but still wrong - you will call your lambda once with 3 arguments, while it only accepts one - and instead, you want to call lambda 3 times with a single argument.

There are several ways to do this. First, and the least preferred, is to call the function recursively, as other answer suggests. This prompts ugly syntax, and also adds burden on compiler for recursive template instantiation. Much better solution is to expand the argument using array trick, for example (ignoring the forward for simplicity):

auto lam = [&f](const auto& a) { f(a); return true;}
bool arr[] = { lam(std::forward<ARG>(arg))... };
(void)arr;

In C++ 17 you can use fold expression to achieve even more neat syntax:

(f(std::forward<ARG>(arg)), ...);
like image 54
SergeyA Avatar answered Mar 13 '26 04:03

SergeyA


Expanding a parameter pack works in contexts that expect a comma separated list.

That is, your code:

f(std::forward<Arg>(arg...));

It attempting to expand into:

f( "Aa", 3, 4 );

And the lambda you have supplied does not support such a call.

To expand a parameter pack into multiple function calls, use a recursive function.

template <class F>
void for_each_argument(F f)
{
    // (No args)
}

template <class F, class FirstArg, class... MoreArgs>
void for_each_argument(F f, FirstArg&& first_arg, MoreArgs&&... more_args)
{
    f( std::forward<FirstArg>(first_arg) );
    for_each_argument( f, std::forward<MoreArgs>(more_args)... );
}
like image 30
Drew Dormann Avatar answered Mar 13 '26 05:03

Drew Dormann