简体   繁体   中英

Variadic templates in C++ and how they work

So I'm trying to understand how variadic templates work, so I have this code:

template <typename Res, typename Type>
void Sum(Res &result, Type &val)
{
    result += val;
}

template <typename Res, typename First, typename... Rest>
void Sum(Res &result, First val1, Rest... valN)
{
    result += val1;
    return Sum(result, valN...);
}

Now my book says that:

the compiler actually creates code for the right kind of Sum() that would suit the call, doing so recursively until all arguments have been processed.

Now I don't understand this, I also tried debugging and I saw that the second Sum function block gets called for each valN value, and then at the last one calls the first Sum() function, this doesnt make any sense for me.

I also learned that to unpack them we need to use function calls inside the variadic template function, is that true?

And why to we have to unpack them, I don't understand, can't we just access them directly in the variadic template function?

Thanks in advance.

I think that you might miss the point that when you step through this in the debugger, you're entering a completely different function each time.
(Note that val1 is not used in the "next" call, so you're passing one argument less each time.)

If there weren't variadic templates, you would need to spell these out as separate templates, but it would be exactly the same:

template <typename Res, typename T>
void Sum(Res& result, T val)
{
    result += val;
}

template <typename Res, typename T1, typename T2>
void Sum(Res& result, T1 val1, T2 val2)
{
    result += val1;
    Sum(result, val2);
}

template <typename Res, typename T1, typename T2, typename T3>    
void Sum(Res& result, T1 val1, T2 val2, T3 val3)
{
    result += val1;
    Sum(result, val2, val3);
}

template <typename Res, typename T1, typename T2, typename T3, typename T4>        
void Sum(Res& result, T1 val1, T2 val2, T3 val3, T4 val4)
{
    result += val1;
    Sum(result, val2, val3, val4);
}

template <typename Res, typename T1, typename T2, typename T3, typename T5>    
void Sum(Res& result, T1 val1, T2 val2, T3 val3, T4 val4, T5 val5)
{
    ...

and so on, as many of them as you need.

The variadic template makes the compiler generate all of these "on demand" and saves you from writing them all out.

One way to look at it is that regular function templates (with only type parameters) generate overloads with the same number of parameters; variadic templates let you also generate overloads with varying amounts of parameters.

Direct access is impossible because you don't know how many elements there inside the parameter pack, what their types are, and they don't have names.
If you want to access a specific argument you also need to make it explicit.

Just imagine that templates work as if there is a function created every time you match one of the template specifications.

  • If you have 4 arguments, the compiler sees that that matches the parameter pack definition, which is calls to the 3 argument version.
  • Then that version needs to be generated as well, but that again calls to the 2 argument version.
  • That one does not match the second definition, but the first, which can be fully resolved.

Function calls are not strictly necessary for unpacking, see Expansions section on cppreference .

Direct access is not possible, because it implies that the parameter pack is not variadic. However, you can always write your version of function directly referring to the template of any index by just providing a template integerand expanding the parameter pack while decrementing the integer (and have a special behaviour defined when it is 0).

[Example for printing out a partial sum on the fifth processed argument of the variadic template]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM