i chik
i chik

Reputation: 104

C++ parameter pack expansion

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...));

Upvotes: 2

Views: 4359

Answers (2)

SergeyA
SergeyA

Reputation: 62553

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)), ...);

Upvotes: 7

Drew Dormann
Drew Dormann

Reputation: 63715

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)... );
}

Upvotes: 5

Related Questions