Reputation: 11
I have a general understanding of template parameters work, but I want to know how they work. Are they like an array of typenames? If you have:
template<typename... Ar>
void f(const Ar&... ar);
How would the compiler know to include the specifiers while also expanding the parameter pack, and applying those specifiers to each element of the parameter pack? And how does ar work?
Essentially I am asking how a compiler implements the template parameter pack and how it executes the packing and unpacking of template parameter packs,( or variadic templates).
Upvotes: 1
Views: 238
Reputation: 21
Why not read the source code? For example, relevant stuff for parameter packs can be found in pt.c in the GCC source code.
parser.c
Here's the code that creates a parameter declarator.
cp_parameter_declarator *no_parameters;
/* Create a parameter declarator with the indicated DECL_SPECIFIERS,
DECLARATOR and DEFAULT_ARGUMENT. */
cp_parameter_declarator *
make_parameter_declarator (cp_decl_specifier_seq *decl_specifiers,
cp_declarator *declarator,
tree default_argument,
bool template_parameter_pack_p = false)
{
cp_parameter_declarator *parameter;
parameter = ((cp_parameter_declarator *)
alloc_declarator (sizeof (cp_parameter_declarator)));
parameter->next = NULL;
if (decl_specifiers)
parameter->decl_specifiers = *decl_specifiers;
else
clear_decl_specs (¶meter->decl_specifiers);
parameter->declarator = declarator;
parameter->default_argument = default_argument;
parameter->template_parameter_pack_p = template_parameter_pack_p;
return parameter;
}
pt.c
Here's the code that creates a pack expansion.
/* Turn ARG, which may be an expression, type, or a TREE_LIST
representation a base-class initializer into a parameter pack
expansion. If all goes well, the resulting node will be an
EXPR_PACK_EXPANSION, TYPE_PACK_EXPANSION, or TREE_LIST,
respectively. */
tree
make_pack_expansion (tree arg)
{
tree result;
tree parameter_packs = NULL_TREE;
bool for_types = false;
struct find_parameter_pack_data ppd;
if (!arg || arg == error_mark_node)
return arg;
if (TREE_CODE (arg) == TREE_LIST && TREE_PURPOSE (arg))
{
/* A TREE_LIST with a non-null TREE_PURPOSE is for a base
class initializer. In this case, the TREE_PURPOSE will be a
_TYPE node (representing the base class expansion we're
initializing) and the TREE_VALUE will be a TREE_LIST
containing the initialization arguments.
The resulting expansion looks somewhat different from most
expansions. Rather than returning just one _EXPANSION, we
return a TREE_LIST whose TREE_PURPOSE is a
TYPE_PACK_EXPANSION containing the bases that will be
initialized. The TREE_VALUE will be identical to the
original TREE_VALUE, which is a list of arguments that will
be passed to each base. We do not introduce any new pack
expansion nodes into the TREE_VALUE (although it is possible
that some already exist), because the TREE_PURPOSE and
TREE_VALUE all need to be expanded together with the same
_EXPANSION node. Note that the TYPE_PACK_EXPANSION in the
resulting TREE_PURPOSE will mention the parameter packs in
both the bases and the arguments to the bases. */
tree purpose;
tree value;
tree parameter_packs = NULL_TREE;
/* Determine which parameter packs will be used by the base
class expansion. */
ppd.visited = new hash_set<tree>;
ppd.parameter_packs = ¶meter_packs;
cp_walk_tree (&TREE_PURPOSE (arg), &find_parameter_packs_r,
&ppd, ppd.visited);
if (parameter_packs == NULL_TREE)
{
error ("base initializer expansion %<%T%> contains no parameter packs", arg);
delete ppd.visited;
return error_mark_node;
}
if (TREE_VALUE (arg) != void_type_node)
{
/* Collect the sets of parameter packs used in each of the
initialization arguments. */
for (value = TREE_VALUE (arg); value; value = TREE_CHAIN (value))
{
/* Determine which parameter packs will be expanded in this
argument. */
cp_walk_tree (&TREE_VALUE (value), &find_parameter_packs_r,
&ppd, ppd.visited);
}
}
delete ppd.visited;
/* Create the pack expansion type for the base type. */
purpose = cxx_make_type (TYPE_PACK_EXPANSION);
SET_PACK_EXPANSION_PATTERN (purpose, TREE_PURPOSE (arg));
PACK_EXPANSION_PARAMETER_PACKS (purpose) = parameter_packs;
/* Just use structural equality for these TYPE_PACK_EXPANSIONS;
they will rarely be compared to anything. */
SET_TYPE_STRUCTURAL_EQUALITY (purpose);
return tree_cons (purpose, TREE_VALUE (arg), NULL_TREE);
}
if (TYPE_P (arg) || TREE_CODE (arg) == TEMPLATE_DECL)
for_types = true;
/* Build the PACK_EXPANSION_* node. */
result = for_types
? cxx_make_type (TYPE_PACK_EXPANSION)
: make_node (EXPR_PACK_EXPANSION);
SET_PACK_EXPANSION_PATTERN (result, arg);
if (TREE_CODE (result) == EXPR_PACK_EXPANSION)
{
/* Propagate type and const-expression information. */
TREE_TYPE (result) = TREE_TYPE (arg);
TREE_CONSTANT (result) = TREE_CONSTANT (arg);
}
else
/* Just use structural equality for these TYPE_PACK_EXPANSIONS;
they will rarely be compared to anything. */
SET_TYPE_STRUCTURAL_EQUALITY (result);
/* Determine which parameter packs will be expanded. */
ppd.parameter_packs = ¶meter_packs;
ppd.visited = new hash_set<tree>;
cp_walk_tree (&arg, &find_parameter_packs_r, &ppd, ppd.visited);
delete ppd.visited;
/* Make sure we found some parameter packs. */
if (parameter_packs == NULL_TREE)
{
if (TYPE_P (arg))
error ("expansion pattern %<%T%> contains no argument packs", arg);
else
error ("expansion pattern %<%E%> contains no argument packs", arg);
return error_mark_node;
}
PACK_EXPANSION_PARAMETER_PACKS (result) = parameter_packs;
PACK_EXPANSION_LOCAL_P (result) = at_function_scope_p ();
return result;
}
Upvotes: 2