How can I have multiple parameter packs in a variadic template?

后端 未结 4 1561
情书的邮戳
情书的邮戳 2020-11-27 12:55

Function one() accepts one parameter pack. Function two() accepts two. Each pack is constrained to be wrapped in types A and B. Why is it

相关标签:
4条回答
  • 2020-11-27 13:22

    Here is another way to have several parameters packs using template template parameters:

    #include <iostream>
    
    template <typename... Types>
    struct foo {};
    
    template < typename... Types1, template <typename...> class T
             , typename... Types2, template <typename...> class V
             , typename U >
    void
    bar(const T<Types1...>&, const V<Types2...>&, const U& u)
    {
      std::cout << sizeof...(Types1) << std::endl;
      std::cout << sizeof...(Types2) << std::endl;
      std::cout << u << std::endl;
    }
    
    int
    main()
    {
      foo<char, int, float> f1;
      foo<char, int> f2;
      bar(f1, f2, 9);
      return 0;
    }
    
    0 讨论(0)
  • 2020-11-27 13:26

    Function templates (like skypjack's example) and partial specializations of class and variable templates can have multiple parameter packs if each template parameter subsequent to a template parameter pack either has a default value or can be deduced. The only thing I'd like to add/point out is that for class and variable templates you need a partial specialization. (See: C++ Templates, The Complete Guide, Vandevoorde, Josuttis, Gregor 12.2.4, Second Edition)

    // A template to hold a parameter pack
    template < typename... >
    struct Typelist {};
    
    // Declaration of a template
    template< typename TypeListOne 
            , typename TypeListTwo
            > 
    struct SomeStruct;
    
    // Specialization of template with multiple parameter packs
    template< typename... TypesOne 
            , typename... TypesTwo
            >
    struct SomeStruct< Typelist < TypesOne... >
                     , Typelist < TypesTwo... >
                     >
    {
            // Can use TypesOne... and TypesTwo... how ever
            // you want here. For example:
            typedef std::tuple< TypesOne... > TupleTypeOne;
            typedef std::tuple< TypesTwo... > TupleTypeTwo;
    };      
    
    0 讨论(0)
  • 2020-11-27 13:26

    The compiler needs a way to know where is the barrier between the two variadic templates. A clean way of doing this is to define one pack of arguments for an object and the second pack for a static member function. This can be appied to more than two variadic templates by nesting multiple structs in eachother. (keeping the last level as a function)

    #include <iostream>
    
    template<typename... First>
    struct Obj
    {
        template<typename... Second>
        static void Func()
        {
            std::cout << sizeof...(First) << std::endl;
            std::cout << sizeof...(Second) << std::endl;
        }
    };
    
    int main()
    {
        Obj<char, char>::Func<char, char, char, char>();
        return 0;
    }
    
    0 讨论(0)
  • 2020-11-27 13:27

    I found one solution. Wrap each parameter pack in a Tuple. Use a struct for partial specialization. Here's a demo that forwards arguments to a functor by consuming one tuple as a list and accumulating another. Well, this one forwards by copying. Tuples are used in type deduction yet no tuples are used in function parameters, which I think is neat.

    #include <iostream>
    #include <tuple>
    
    template < typename ... >
    struct two_impl {};
    
    // Base case
    template < typename F,
               typename ...Bs >
    struct two_impl < F, std::tuple <>, std::tuple< Bs... > >  {
      void operator()(F f, Bs... bs) {
        f(bs...);
      }
    };
    
    // Recursive case
    template < typename F,
               typename A,
               typename ...As,
               typename ...Bs >
    struct two_impl < F, std::tuple< A, As... >, std::tuple< Bs...> >  {
      void operator()(F f, A a, As... as, Bs... bs) {
        auto impl = two_impl < F, std::tuple < As... >, std::tuple < Bs..., A> >();
        impl(f, as..., bs..., a);
      }
    };
    
    template < typename F, typename ...Ts >
    void two(F f, Ts ...ts) {
      auto impl = two_impl< F, std::tuple < Ts... >, std::tuple <> >();
      impl(f, ts...);
    }
    
    struct Test {
      void operator()(int i, float f, double d) {
        std::cout << i << std::endl << f << std::endl << d << std::endl;
      }
    };
    
    int main () {
      two(Test(), 1, 1.5f, 2.1);
    }
    

    Tuples are a very good compile time list.

    0 讨论(0)
提交回复
热议问题