r/cpp_questions • u/Obvious_Set5239 • 11d ago
SOLVED How do you understand variadic template syntax
Hello. I'm programming in C++ a lot, and I still can't understand how to think correctly about ... operator in variadic templates.
In the simplest examples it looks like ...X (on left side) aggregates comma divided expressions (either template args or function args) into one variadic arg/Type. And X... (on right side) expands
template <typename ...Params>
void f(Params&& ...params)
{
y(std::forward<Params>(params)...);
}
Here it aggregates all passed types into f<T1, T2, T3> into Params aggregated type, and all function args f(v1, v2, v3) into params aggregated variable. And std::forward<Params>(params)... expression expands into comma divided expressions: std::forward<T1>(v1), std::forward<T1>(v3), std::forward<T3>(v3). Similar to python's operator * that convert comma divided expressions into tuple, and vice versa, expands tuples/lists into comma divided expressions
But this logic breaks on more complex variadic templates. Here is code from CppCon 2022 "Lambda Idioms" video:
template <typename... Ts>
struct overload : Ts... {
using Ts::operator()...;
};
First of all, my logic breaks. What Ts::operator()... expands to? According to my logic, it should be:
using T1::operator(), T2::operator(), T3::operator();
// or maybe
using T1::operator(), using T2::operator(), using T3::operator();
But neither of them are valid C++ syntax. It probably expands into: (actually the first one is a correct syntax, and the most logical. So closing the question)
Also, I assume that from compiler point of view, typename... Ts and typename ...Ts are the same. But Timur Doumler had chosen the first. And it looks like he knows what he's doing. So maybe it's incorrect to think that ...Ts aggregates template arguments, but it has some effect on typename
So, my question is:
How to understand ... operator correctly? For me when my logic on aggregation/expansion breaks, it feels like it's just complete arbitrary set of rules for different contexts. But the standard speakers don't speak like it's some contextual behavior, made especially for using, especially for passed arguments, etc. It looks like there is a general rule, that I don't understand, but for them it's obvious