【问题标题】:How to create the Cartesian product of a type list?如何创建类型列表的笛卡尔积?
【发布时间】:2012-02-25 16:19:11
【问题描述】:

我想使用可变参数模板创建类型列表的叉积。

这是我目前所拥有的:

#include <iostream>
#include <typeinfo>
#include <cxxabi.h>

template<typename...> struct type_list {};

template<typename T1, typename T2> struct type_pair {};

template<typename T, typename... Rest>
  struct row
{
  typedef type_list<type_pair<T,Rest>...> type;
};

template<typename... T>
  struct cross_product
{
  typedef type_list<typename row<T,T...>::type...> type;
};

int main()
{
  int s;
  typedef cross_product<int, float, short>::type result;
  std::cout << abi::__cxa_demangle(typeid(result).name(), 0, 0, &s) << std::endl;

  return 0;
}

这个程序输出:

$ g++ -std=c++0x cross_product.cpp ; ./a.out 
type_list<type_list<type_pair<int, int>, type_pair<int, float>, type_pair<int, short> >, type_list<type_pair<float, int>, type_pair<float, float>, type_pair<float, short> >, type_list<type_pair<short, int>, type_pair<short, float>, type_pair<short, short> > >

但我希望它输出:

type_list<type_pair<int,int>, type_pair<int,float>, type_pair<int,short>, type_pair<float,int>,...>

也就是说,没有嵌套的type_lists。

如果没有 row 帮助器,是否有直接的方法可以做到这一点,或者解决方案是否应该以某种方式“解开”嵌套的 type_lists?

【问题讨论】:

  • __cxa_demangle在下面调用malloc,所以你对free the memory负责。
  • 我们都知道这个问题的出处。 知道这是安德烈给我们的家庭作业。 :)
  • 以下解决方案之一可行,但我不知道它是否会像 Andrei 的“保证金报价”预期的那样简洁优雅。
  • @R.MartinhoFernandes Andrei 是否亲自或在任何博客上给过他作业,链接?
  • @Mr.Anubis 来自他的 GoingNative 演讲。

标签: c++ c++11 variadic-templates


【解决方案1】:

我认为一个不错的干净版本:

cross_product.cpp:

#include "type_printer.hpp"

#include <iostream>

template<typename ...Ts> struct type_list {};
template<typename T1, typename T2> struct pair {};

// Concatenation
template <typename ... T> struct concat;
template <typename ... Ts, typename ... Us>
struct concat<type_list<Ts...>, type_list<Us...>>
{
    typedef type_list<Ts..., Us...> type;
};

// Cross Product
template <typename T, typename U> struct cross_product;

// Partially specialise the empty case for the first type_list.
template <typename ...Us>
struct cross_product<type_list<>, type_list<Us...>> {
    typedef type_list<> type;
};

// The general case for two type_lists. Process:
// 1. Expand out the head of the first type_list with the full second type_list.
// 2. Recurse the tail of the first type_list.
// 3. Concatenate the two type_lists.
template <typename T, typename ...Ts, typename ...Us>
struct cross_product<type_list<T, Ts...>, type_list<Us...>> {
    typedef typename concat<
        type_list<pair<T, Us>...>,
        typename cross_product<type_list<Ts...>, type_list<Us...>>::type
    >::type type;
};

struct A {};
struct B {};
struct C {};
struct D {};
struct E {};
struct F {};

template <typename T, typename U>
void test()
{
    std::cout << print_type<T>() << " \u2a2f " << print_type<U>() << " = "
        << print_type<typename cross_product<T, U>::type>() << std::endl;
}

int main()
{
    std::cout << "Cartesian product of type lists\n";
    test<type_list<>, type_list<>>();
    test<type_list<>, type_list<A>>();
    test<type_list<>, type_list<A, B>>();
    test<type_list<A, B>, type_list<>>();
    test<type_list<A>, type_list<B>>();
    test<type_list<A>, type_list<B, C, D>>();
    test<type_list<A, B>, type_list<B, C, D>>();
    test<type_list<A, B, C>, type_list<D>>();
    test<type_list<A, B, C>, type_list<D, E, F>>();
    return 0;
}

type_printer.hpp:

#ifndef TYPE_PRINTER_HPP
#define TYPE_PRINTER_HPP

#include "detail/type_printer_detail.hpp"

template <typename T>
std::string print_type()
{
    return detail::type_printer<T>()();
}

#endif

detail/type_printer_detail.hpp:

#ifndef DETAIL__TYPE_PRINTER_DETAIL_HPP
#define DETAIL__TYPE_PRINTER_DETAIL_HPP

#include <typeinfo>
#include <cxxabi.h>
#include <string>

template <typename ...Ts> struct type_list;
template <typename T1, typename T2> struct pair;

namespace detail {

// print scalar types
template <typename T>
struct type_printer {
    std::string operator()() const {
        int s;
        return abi::__cxa_demangle(typeid(T).name(), 0, 0, &s);
    }   
};

// print pair<T, U> types
template <typename T, typename U>
struct type_printer<pair<T, U>> {
    std::string operator()() const {
        return "(" + type_printer<T>()() + "," + type_printer<U>()() + ")";
    }   
};

// print type_list<T>
template <>
struct type_printer<type_list<>> {
    std::string operator()() const {
        return "\u2205";
    }   
};

template <typename T>
struct type_printer<type_list<T>> {
    std::string operator()() const {
        return "{" + type_printer<T>()() + "}";
    }   
    std::string operator()(const std::string& sep) const {
        return sep + type_printer<T>()();
    }   
};

template <typename T, typename ...Ts>
struct type_printer<type_list<T, Ts...>> {
    std::string operator()() const {
        return "{" + type_printer<T>()() + type_printer<type_list<Ts...>>()(std::string(", ")) + "}";
    }   
    std::string operator()(const std::string& sep) const {
        return sep + type_printer<T>()() + type_printer<type_list<Ts...>>()(sep);
    }   
};
}

#endif

运行:

g++ -std=c++0x cross_product.cpp && ./a.out

输出:

Cartesian product of type lists
∅ ⨯ ∅ = ∅
∅ ⨯ {A} = ∅
∅ ⨯ {A, B} = ∅
{A, B} ⨯ ∅ = ∅
{A} ⨯ {B} = {(A,B)}
{A} ⨯ {B, C, D} = {(A,B), (A,C), (A,D)}
{A, B} ⨯ {B, C, D} = {(A,B), (A,C), (A,D), (B,B), (B,C), (B,D)}
{A, B, C} ⨯ {D} = {(A,D), (B,D), (C,D)}
{A, B, C} ⨯ {D, E, F} = {(A,D), (A,E), (A,F), (B,D), (B,E), (B,F), (C,D), (C,E), (C,F)}

(我注意到在使用 Chrome 的 Windows 上,交叉乘积 unicode 字符无法正常显示。抱歉,我不知道如何解决。)

【讨论】:

  • 非常流畅且极简。赞一个!
【解决方案2】:

不知何故,我的大脑被炸了:我认为我使用的代码比需要的多,但至少它有预期的结果(尽管我没有修复内存泄漏):

#include <iostream>
#include <typeinfo>
#include <cxxabi.h>

template<typename...> struct type_list {};

template<typename T1, typename T2> struct type_pair {};

template<typename T, typename... Rest>
  struct row
{
  typedef type_list<type_pair<T,Rest>...> type;
};

template <typename... T> struct concat;
template <typename... S, typename... T>
struct concat<type_list<S...>, type_list<T...>>
{
    typedef type_list<S..., T...> type;
};

template <typename... T>
struct expand
{
    typedef type_list<T...> type;
};
template <> struct expand<> { typedef type_list<> type; };
template <typename... T, typename... L>
struct expand<type_list<T...>, L...>
{
    typedef typename concat<typename expand<T...>::type, typename expand<L...>::type>::type type;
};

template<typename... T>
  struct cross_product
{
    typedef typename expand<type_list<typename row<T,T...>::type...>>::type type;

};

int main()
{
  int s;
  typedef cross_product<int, float, short>::type result;
  std::cout << abi::__cxa_demangle(typeid(result).name(), 0, 0, &s) << std::endl;

  return 0;
}

【讨论】:

    【解决方案3】:

    可能是这样的:

    template <typename ...Args> struct typelist { };
    
    template <typename S, typename T> struct typelist_cat;
    
    template <typename ...Ss, typename ...Ts>
    struct typelist_cat<typelist<Ss...>, typelist<Ts...>>
    {
        typedef typelist<Ss..., Ts...> type;
    };
    
    
    template <typename S, typename T> struct product;
    
    template <typename S, typename ...Ss, typename ...Ts>
    struct product<typelist<S, Ss...>, typelist<Ts...>>
    {
        // the cartesian product of {S} and {Ts...}
        // is a list of pairs -- here: a typelist of 2-element typelists
        typedef typelist<typelist<S, Ts>...> S_cross_Ts;
    
        // the cartesian product of {Ss...} and {Ts...} (computed recursively)
        typedef typename product<typelist<Ss...>, typelist<Ts...>>::type
            Ss_cross_Ts;
    
        // concatenate both products
        typedef typename typelist_cat<S_cross_Ts, Ss_cross_Ts>::type type;
    };
    
    // end the recursion
    template <typename ...Ts>
    struct product<typelist<>, typelist<Ts...>>
    {
        typedef typelist<> type;
    };
    

    现在你应该可以使用product&lt;typelist&lt;A,B,C&gt;, typelist&lt;D,E,F&gt;&gt;::type了。

    【讨论】:

    • typename T 没有为第 3 个和第 4 个模板定义。
    • 不编译。它不使用类型对来配对类型以形成笛卡尔积,所以我不知道它是如何工作的。
    • @keveman:是的,还有一个错误,现在已修复。原来需要一个连接器,我第一次忘记了。它是与 2 乘以 3 为 6 相同的乘积,而不是 {(0,0), (0,1), (0,2), (1,0), (1,1), (1, 2)}。如果 OP 实际上 想要 对,他应该这么说。
    • @KerrekS:我不知道这个问题的标题是否已更改,但操作确实要求the cartesian product,这意味着对
    • @cheshirekow,笛卡尔积的笛卡尔幂不限于平方,即结果不一定是对。
    【解决方案4】:

    C++17

    Working Demo

    逻辑连接 type_lists 以避免嵌套 type_list 就像你要求的那样:

    // base case: 2 type_lists
    template<class... Ts, class... Us>
    auto concat(type_list<Ts...>, type_list<Us...>) -> type_list<Ts..., Us...>;
    
    // recursive case: more than 2 type_lists
    template<class... Ts, class... Us, class... Rest>
    auto concat(type_list<Ts...>, type_list<Us...>, Rest...) -> decltype(concat(type_list<Ts..., Us...>{}, Rest{}...));
    

    请注意,这些函数没有(或不需要)实现;这是一个避免类模板特化的技巧(我从 Hana Dusikova 的compile time regular expressions 那里学到的)

    然后,将您的 rowcross_product impls 分别简化为 pairscross_product_impl

    template<class T, class... Ts>
    using pairs = type_list<type_pair<T, Ts>...>;
    
    template<class... T>
    auto cross_product_impl()
    {
        if constexpr(sizeof...(T) == 0)
            return type_list<> {};
        if constexpr(sizeof...(T) == 1)
            return type_list<type_pair<T, T>...>{};
        if constexpr(sizeof...(T) > 1)
            return concat(pairs<T, T...>{}...);
    }
    

    if constexpr 让我们更容易表达逻辑,我想。

    最后是cross_product 的类型别名,它告诉我们理论上调用cross_product_impl 时的类型:

    template<class... T>
    using cross_product = decltype(cross_product_impl<T...>());
    

    用法和以前基本一样:

    cross_product<int, float, short> result;
    

    【讨论】:

      【解决方案5】:

      这是另一个解决方案。

      #include <iostream>
      #include <typeinfo>
      #include <cxxabi.h>
      
      template <typename ...Args> struct typelist { };
      template <typename, typename> struct typepair { };
      
      template <typename S, typename T> struct product;
      template <typename S, typename T> struct append;
      
      template<typename ...Ss, typename ...Ts>
      struct append<typelist<Ss...>, typelist<Ts...>> {
        typedef typelist<Ss..., Ts...> type;
      };
      
      template<>
      struct product<typelist<>, typelist<>> {
        typedef typelist<> type;
      };
      
      template<typename ...Ts>
      struct product<typelist<>, typelist<Ts...>> {
        typedef typelist<> type;
      };
      
      template<typename ...Ts>
      struct product<typelist<Ts...>, typelist<>> {
        typedef typelist<> type;
      };
      
      template<typename S, typename T, typename ...Ss, typename ...Ts>
      struct product<typelist<S, Ss...>, typelist<T, Ts...>> {
        typedef typename
                append<typelist<typepair<S, T>,
                                typepair<S, Ts>...,
                                typepair<Ss, T>...>,
              typename product<typelist<Ss...>, typelist<Ts...>>::type>::type type;
      };
      
      int main(void)
      {
        int s;
        std::cout << abi::__cxa_demangle(
        typeid(product<typelist<int, float>, typelist<short, double>>::type).name(), 0, 0, &s)     << "\n";
        return 0;
      }
      

      【讨论】:

        【解决方案6】:

        到目前为止,所有解决方案都有缺点、不必要的依赖项、不必要的帮助程序,并且都仅限于笛卡尔二次幂。以下解决方案没有此类缺点和支持:

        1. 任何笛卡尔幂,包括 0。
        2. 如果任何因子为空集,则返回空集。
        3. 代码是自包含的,不依赖于任何包含文件。
        4. 函数的输入可以是任何模板类型。
        5. 可以通过第一个模板指定输出列表的类型 参数。

        然后我想,它实际上更难实施(但作为家庭作业很好)。我实际上正在考虑创建一个小生成器,它允许我扩展模板语法,从而使这些事情变得非常容易。

        简化的代码工作如下:product 将输入列表tuple&lt;A...&gt;,tuple&lt;B...&gt;,tuple&lt;C...&gt; 转换为tuple&lt;tuple&lt;A&gt;...&gt;, tuple&lt;B...&gt;, tuple&lt;C...&gt;。然后将第二个列表传递给执行递归笛卡尔积计算的product_helper

        template <typename... T> struct cat2;
        
        template <template<typename...> class R, typename... As, typename... Bs>
        struct cat2 <R<As...>, R<Bs...> > {
                using type = R <As..., Bs...>;
        };
        
        template <typename... Ts> struct product_helper;
        
        template <template<typename...> class R, typename... Ts>
        struct product_helper < R<Ts...> > { // stop condition
                using type = R< Ts...>;
        };
        
        template <template<typename...> class R, typename... Ts>
        struct product_helper < R<R<> >, Ts... > { // catches first empty tuple
                using type = R<>;
        };
        
        template <template<typename...> class R, typename... Ts, typename... Rests>
        struct product_helper < R<Ts...>, R<>, Rests... > { // catches any empty tuple except first
                using type = R<>;
        };
        
        template <template<typename...> class R, typename... X, typename H, typename... Rests>
        struct product_helper < R<X...>, R<H>, Rests... > {
                using type1 = R <typename cat2<X,R<H> >::type...>;
                using type  = typename product_helper<type1, Rests...>::type;
        };
        
        template <template<typename...> class R, typename... X, template<typename...> class Head, typename T, typename... Ts, typename... Rests>
        struct product_helper < R<X...>, Head<T, Ts...>, Rests... > {
                using type1 = R <typename cat2<X,R<T> >::type...>;
                using type2 = typename product_helper<R<X...> , R<Ts...> >::type;
                using type3 = typename cat2<type1,type2>::type;
                using type  = typename product_helper<type3, Rests...>::type;
        };
        
        template <template<typename...> class R, typename... Ts> struct product;
        
        template <template<typename...> class R>
        struct product < R > { // no input, R specifies the return type
            using type = R<>;
        };
        
        template <template<typename...> class R, template<typename...> class Head, typename... Ts, typename... Tail>
        struct product <R, Head<Ts...>, Tail... > { // R is the return type, Head<A...> is the first input list
            using type = typename product_helper< R<R<Ts>...>, Tail... >::type;
        };
        

        这里是compilable example,说明如何使用代码。

        【讨论】:

        • 在我看来你可以把cat2写成template&lt;class A, class B&gt; struct cat2; template&lt; template&lt;class...&gt; class R, class... A, class... B&gt; struct&lt;R&lt;A...&gt;, R&lt;B...&gt;&gt; { .. };
        【解决方案7】:

        注意:这 不是 OP 要求的...但可能与偶然发现此问题的其他人(如我)有关。以下是使用Loki::TypeList(即以前的 C++-11)的方法,可能出于历史兴趣或出于兼容性考虑。

        另外,也许我污染 loki 的命名空间是冒昧的。 YMMV。

        crossproduct.h

        #include "loki/NullType.h"
        #include "loki/Typelist.h"
        
        namespace Loki {
        namespace   TL {
        
        /// a pair of two types
        template <typename A_t, typename B_t>
        struct TypePair
        {
            typedef A_t A;
            typedef B_t B;
        };
        
        
        /// a template which takes one type and pairs it with all other types
        /// in another typelist
        template <class T, class TList > struct DistributePair;
        
        /// specialization of Distribute for the nulltype
        template < class TList >
        struct DistributePair< NullType, TList >
        {
             typedef NullType type;
        };
        
        
        /// specialization of Distribute where the second parameter is nulltype
        template <class T >
        struct DistributePair< T, NullType >
        {
             typedef NullType type;
        };
        
        /// specialization of Distribute where the first parameter is a
        /// typelist
        template <class T, class Head, class Tail >
        struct DistributePair< T, Typelist<Head,Tail> >
        {
             typedef Typelist<
                         TypePair<T,Head>,
                         typename DistributePair<T,Tail>::type
                             > type;
        };
        
        /// performs cartesion product of two typelists
        template <class TListA, class TListB> struct CrossProduct;
        
        /// specialization of CrossProduct for NullType
        template <class TListB>
        struct CrossProduct< NullType, TListB >
        {
            typedef NullType type;
        };
        
        /// specialization of CrossProduct for recursion
        template <class Head, class Tail, class TListB>
        struct CrossProduct< Typelist<Head,Tail>, TListB >
        {
            typedef typename Append<
                    typename DistributePair< Head,TListB >::type,
                    typename CrossProduct< Tail, TListB >::type
                      >::Result type;
        };
        
        } // namespace TL
        } // namespace Loki
        

        test.cpp

        #include <crossproduct.h>
        #include <loki/HierarchyGenerators.h>
        #include <iostream>
        
        struct A{};
        struct B{};
        struct C{};
        
        struct D{};
        struct E{};
        struct F{};
        
        typedef LOKI_TYPELIST_3(A,B,C)  TypeListA_t;
        typedef LOKI_TYPELIST_3(D,E,F)  TypeListB_t;
        
        typedef typename Loki::TL::CrossProduct< TypeListA_t, TypeListB_t >::type Cross_t;
        
        template <typename T> const char* toString();
        
        template <> const char* toString<A>(){ return "A"; };
        template <> const char* toString<B>(){ return "B"; };
        template <> const char* toString<C>(){ return "C"; };
        template <> const char* toString<D>(){ return "D"; };
        template <> const char* toString<E>(){ return "E"; };
        template <> const char* toString<F>(){ return "F"; };
        
        template <typename T> struct Printer
        {
            Printer()
            {
                std::cout << toString<T>() << ", ";
            }
        };
        
        template <typename T1, typename T2>
        struct Printer< Loki::TL::TypePair<T1,T2> >
        {
            Printer()
            {
                std::cout << "(" << toString<T1>() << "," << toString<T2>() << "), ";
            }
        };
        
        
        typedef Loki::GenScatterHierarchy< TypeListA_t, Printer > PrinterA_t;
        typedef Loki::GenScatterHierarchy< TypeListB_t, Printer > PrinterB_t;
        typedef Loki::GenScatterHierarchy< Cross_t, Printer >     PrinterCross_t;
        
        int main(int argc, char** argv)
        {
            std::cout << "\nType list A: \n   ";
            PrinterA_t a;
            std::cout << "\nType list B: \n   ";
            PrinterB_t b;
            std::cout << "\nType list Cross: \n   ";
            PrinterCross_t cross;
        
            return 0;
        }
        

        输出

        Type list A: 
           A, B, C, 
        Type list B: 
           D, E, F, 
        Type list Cross: 
           (A,D), (A,E), (A,F), (B,D), (B,E), (B,F), (C,D), (C,E), (C,F), 
        

        【讨论】:

          【解决方案8】:

          真的很喜欢这个“家庭作业”:)

          以下两种解决方案都创建了一个充满 type_list 类型定义的类,以及将检查给定类型列表是否作为 type_list 存在于类中的成员函数。

          第一个解决方案为每个 type_list 创建从 1 到 N 类型的所有可能的类型组合(width 参数定义 N)。第二种解决方案只创建类型对。

          第一个解决方案

          template<typename... Ts> struct type_list { typedef type_list<Ts...> type; };
          
          template<size_t, typename...> struct xprod_tlist_ {};
          
          template<typename... Ts, typename... Us>
          struct xprod_tlist_<1, type_list<Ts...>, Us...> {};
          
          template<size_t width, typename... Ts, typename... Us>
          struct xprod_tlist_<width, type_list<Ts...>, Us...>
          : type_list<Ts..., Us>...
          , xprod_tlist_<width - 1, type_list<Ts..., Us>, Us...>... {};
          
          template<size_t width, typename... Ts> struct xprod_tlist
          : type_list<Ts>..., xprod_tlist_<width, type_list<Ts>, Ts...>... {
              template<typename... Us> struct exists
              : std::is_base_of<type_list<Us...>, xprod_tlist<width, Ts...>> {};
          
              template<typename... Us> struct assert_exists {
                  static_assert(exists<Us...>::value, "Type not present in list");
              };
          };
          

          用法:

          typedef xprod_tlist<5, int, char, string, float, double, long> X;
          
          //these pass
          X::assert_exists<int, int, int, int, int> assert_test1;
          X::assert_exists<double, float, char, int, string> assert_test2;
          
          //these fail
          X::assert_exists<char, char, char, char, char, char> assert_test3;
          X::assert_exists<int, bool> assert_test4;
          
          //true
          auto test1 = X::exists<int, int, int, int, int>::value;
          auto test2 = X::exists<double, float, char, int, string>::value;
          
          //false
          auto test3 = X::exists<char, char, char, char, char, char>::value;
          auto test4 = X::exists<int, bool>::value;
          

          第二种解决方案

          template<class T, class U> struct type_pair { typedef type_pair<T, U> type; };
          template<class... Ts> struct type_list {};
          template<class...> struct xprod_tlist_ {};
          
          template<class T, class... Ts, class... Us>
          struct xprod_tlist_<type_list<T, Ts...>, type_list<Us...>>
          : type_pair<T, Us>..., xprod_tlist_<type_list<Ts...>, type_list<Us...>> {};
          
          template<class... Ts>
          struct xprod_tlist : xprod_tlist_<type_list<Ts...>, type_list<Ts...>> {
              template<class T, class U> struct exists
              : std::is_base_of<type_pair<T, U>, xprod_tlist<Ts...>> {};
          
              template<class T, class U> struct assert_exists {
                  static_assert(exists<T, U>::value, "Type not present in list");
              };
          };
          

          用法:

          typedef xprod_tlist<int, float, string> X;
          
          //these pass
          X::assert_exists<int, int> assert_test1;
          X::assert_exists<int, float> assert_test2;
          X::assert_exists<int, string> assert_test3;
          X::assert_exists<float, int> assert_test4;
          X::assert_exists<float, float> assert_test5;
          X::assert_exists<float, string> assert_test6;
          X::assert_exists<string, int> assert_test7;
          X::assert_exists<string, float> assert_test8;
          X::assert_exists<string, string> assert_test9;
          
          //this fails
          X::assert_exists<int, char> assert_test10;
          
          //true
          auto test1 = X::exists<int, int>::value;
          auto test2 = X::exists<int, float>::value;
          auto test3 = X::exists<int, string>::value;
          auto test4 = X::exists<float, int>::value;
          auto test5 = X::exists<float, float>::value;
          auto test6 = X::exists<float, string>::value;
          auto test7 = X::exists<string, int>::value;
          auto test8 = X::exists<string, float>::value;
          auto test9 = X::exists<string, string>::value;
          
          //false
          auto test10 = X::exists<int, char>::value;
          

          【讨论】:

            【解决方案9】:

            对于Boost.Mp11,这是一个简短的单行字(一如既往):

            using input = type_list<int, float, short>;
            using result = mp_product<
                type_pair,
                input, input>;
            

            Demo.


            我们可以将其概括为从该输入中重复选择 N 事物。我们不能再使用type_pair 来对我们的元素进行分组,所以我们只有一个type_list 元素列表。为此,我们基本上需要编写:

            mp_product<type_list, input, input, ..., input>
            //                    ~~~~~~~ N times ~~~~~~~~
            

            也同:

            mp_product_q<mp_quote<type_list>, input, input, ..., input>
            //                                ~~~~~~~ N times ~~~~~~~~
            

            一种方法是:

            template <int N>
            using product = mp_apply<
                mp_product_q,
                mp_append<
                    mp_list<mp_quote<type_list>>, 
                    mp_repeat_c<mp_list<input>, N>
                    >>;
            

            Demo.

            【讨论】:

              猜你喜欢
              • 2020-04-12
              • 2014-06-18
              • 2017-11-12
              • 2012-03-24
              • 2018-04-17
              • 1970-01-01
              • 2021-10-22
              • 2019-08-26
              • 2012-08-27
              相关资源
              最近更新 更多