Home > database >  What is function with multiple variadic args?
What is function with multiple variadic args?

Time:10-29

I don't understand how this code works. Could anyone please enlighten me a bit. I was pretty much sure "the parameter pack should be the last argument"

void foo(auto&&...args1, auto&&... args2, auto&&... args3) {
    std::cout << "args1:\n", ((std::cout << args1 << " "), ...);
    std::cout << "args2:\n", ((std::cout << args2 << " "), ...);
    std::cout << "args3:\n", ((std::cout << args3 << " "), ...);
}

int main(int argc, char** argv)
{
    foo(1,2,3,4,5,6);
}

If it's allowed how can I split arg1, args2 and args3?

The compiler (g -11) assumes all parameters pack except args3 are empty, so the output is

args1:
args2:
args3:
1 2 3 4 5 6

CodePudding user response:

The program is ill-formed and gcc and clang are wrong in accepting the code. You can also confirm this by slightly modifying your code to as shown below. There is also an old gcc bug for this.

Basically, in case of function templates(in the modifed program shown below) multiple template parameter packs are permitted, as long as each template parameter following a template parameter pack has either a default value or it can be deduced. But since neither of these requirements is satisfied for T2 and T3, the program is ill-formed.

The exact same reasoning applies to your given example as foo(in your original example) is a generic function(aka abbreviated function template).

GCC and Clang shows the same incorrect behavior for the following modified program: Demo

template<typename... T1, typename... T2, typename... T3>
void foo(T1&&...args1, T2&&... args2, T3&&... args3) {
    std::cout << "args1:\n", ((std::cout << args1 << " "), ...);
    std::cout << "args2:\n", ((std::cout << args2 << " "), ...);
    std::cout << "args3:\n", ((std::cout << args3 << " "), ...);
}

int main(int argc, char** argv)
{
    foo(1,2,3,4,5,6); //gcc and clang compiles this while msvc correctly rejects this
}

Here is the gcc bug:

GCC accepts invalid program involving multiple template parameter packs

Here is the clang bug:

Clang accepts invalid program involving multiple template parameter packs


The same can also be seen from temp.param:

A template parameter pack of a function template shall not be followed by another template parameter unless that template parameter can be deduced from the parameter-type-list ([dcl.fct]) of the function template or has a default argument ([temp.deduct]).

// U can be neither deduced from the parameter-type-list nor specified
template<class... T, class... U> void f() { }   // error

(emphasis mine)

CodePudding user response:

If it's allowed how can I split arg1, args2 and args3?

You can't. What you can do is collect template packs into a single argument, with std::tuple

template <typename... Args1, typename... Args2, typename... Args3>
void foo(std::tuple<Args1...> args1, std::tuple<Args2...> args2, std::tuple<Args3...> args3) {
    auto out = []<std::size_t... Is>(std::string name, auto tup, std::index_sequence<Is...>)
    {
        std::cout << name << " ";
        ((std::cout << get<Is>(tup) << " "), ...);
        std::cout << std::endl;
    };
    
    out("args1", args1, std::index_sequence_for<Args1...>{});
    out("args2", args2, std::index_sequence_for<Args2...>{});
    out("args3", args3, std::index_sequence_for<Args3...>{});
}

See it live

  • Related