C ++ Variadic模板如何在内部工作

时间:2015-08-29 23:59:36

标签: c++ templates compiler-construction variadic-templates

我对模板参数工作有一般的了解,但我想知道它们是如何工作的。它们是不是一个类型名称?如果你有:

template<typename... Ar>
void f(const Ar&... ar);

编译器如何知道在扩展参数包的同时包含说明符,并将这些说明符应用于参数包的每个元素? ar是如何工作的?

基本上我问的是编译器如何实现模板参数包以及它如何执行模板参数包(或可变参数模板)的打包和解包。

1 个答案:

答案 0 :(得分:2)

为什么不读源代码?例如,参数包的相关内容可以在GCC源代码的pt.c中找到。

parser.c

这是创建参数声明符的代码。

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 (&parameter->decl_specifiers);
  parameter->declarator = declarator;
  parameter->default_argument = default_argument;
  parameter->template_parameter_pack_p = template_parameter_pack_p;

  return parameter;
}

pt.c

这是创建包扩展的代码。

/* 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 = &parameter_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 = &parameter_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;
}