【问题标题】:Using template parameter pack instead of macro使用模板参数包而不是宏
【发布时间】:2017-10-08 21:22:39
【问题描述】:

我正在阅读 Andrei Alexandrescu 的 Modern C++ design,我正在尝试使用他提供的一些类型列表示例。在下面的示例中,我想创建一个包含类型和整数的 Option 结构列表。稍后我想创建这些选项的类型列表,然后将其与整数一起传递给另一个结构FindTypeForMapping。如果整数匹配选项列表中的任何整数集,则表达式应计算为该选项的类型,否则应计算为我的自定义类型NullType

第一种可行的方法是 OptionsList 是使用宏创建的,并且对于列表包含的每个数量的 Options,我都有宏,其中 n Options 的每个宏都使用n-1Options 的宏。

然后我想在模板参数列表中使用一个参数包。此版本的列表名为OptionsList2。在OptionsList2 中,我递归地建立了列表,但是在将此列表传递给FindTypeForMapping 时出现编译时错误(见下文)。

struct NullType { };

template<class T, class U>
struct OptionsList
{
  typedef T Head;
  typedef U Tail;
};

template<class T, class... U>
struct OptionsList2
{
  typedef T Head;
  typedef typename std::conditional<sizeof...(U) == 0, NullType, OptionsList2<U...>>::type Tail;
};

template<int n, typename N>
struct Option
{
  enum {
    int_mapping = n
  };
  typedef N MappedType;
};

template<int, int> struct CheckMappedInt;

template<int n>
struct CheckMappedInt<n, n>
{
  enum { is_the_same = 1};
};

template<int n, int m>
struct CheckMappedInt
{
  enum { is_the_same = 0};
};

template<typename OLT, int n> struct FindTypeForMapping;

template<int n>
struct FindTypeForMapping<NullType, n>
{
  typedef NullType mapped_type;
};


template<typename OP, typename Tail, int n>
struct FindTypeForMapping<OptionsList<OP, Tail>, n>
{
 private:
  enum {temp  = CheckMappedInt<OP::int_mapping, n>::is_the_same };
  typedef typename FindTypeForMapping<Tail, n>::mapped_type temp_type;
 public:
  typedef typename std::conditional<
          temp == 1,
          typename OP::MappedType,
          temp_type>::type mapped_type;
};

// Added this after SoryTellers comment
template<typename OP, typename Tail, int n>
struct FindTypeForMapping<OptionsList2<OP, Tail>, n>
{
 private:
  enum {temp  = CheckMappedInt<OP::int_mapping, n>::is_the_same };
  typedef typename FindTypeForMapping<Tail, n>::mapped_type temp_type;
 public:
  typedef typename std::conditional<
          temp == 1,
          typename OP::MappedType,
          temp_type>::type mapped_type;
};

#define OPTION_LIST_1(op1)                                          OptionsList<op1, NullType>
#define OPTION_LIST_2(op1, op2)                                     OptionsList<op1, OPTION_LIST_1(op2)>
#define OPTION_LIST_3(op1, op2, op3)                                OptionsList<op1, OPTION_LIST_2(op2, op3)>
#define OPTION_LIST_4(op1, op2, op3, op4)                           OptionsList<op1, OPTION_LIST_3(op2, op3, op4)>
#define OPTION_LIST_5(op1, op2, op3, op4, op5)                      OptionsList<op1, OPTION_LIST_4(op2, op3, op4, op5)>
#define OPTION_LIST_6(op1, op2, op3, op4, op5, op6)                 OptionsList<op1, OPTION_LIST_5(op2, op3, op4, op5, op6)>
#define OPTION_LIST_7(op1, op2, op3, op4, op5, op6, op7)            OptionsList<op1, OPTION_LIST_6(op2, op3, op4, op5, op6, op7)>
#define OPTION_LIST_8(op1, op2, op3, op4, op5, op6, op7, op8, op9)  OptionsList<op1, OPTION_LIST_7(op2, op3, op4, op5, op6, op7, op8)>
#define OPTION_LIST_9(op1, op2, op3, op4, op5, op6, op7, op8, op9)  OptionsList<op1, OPTION_LIST_8(op2, op3, op4, op5, op6, op7, op8, op9)>


int main(int argc, char* argv[])
{
  typedef Option<1, char> o1;
  typedef Option<2, int> o2;

  // Works
  typedef OPTION_LIST_2(o1, o2) ol;
  typedef typename FindTypeForMapping<ol, 1>::mapped_type ResolvedType; // Works

  typedef OptionsList2<o1, o2> ol2;
  typedef typename FindTypeForMapping<ol2, 1>::mapped_type ResolvedType2;
  /*
 error: invalid use of incomplete type ‘struct FindTypeForMapping<Option<2, int>, 1>’
   typedef typename FindTypeForMapping<Tail, n>::mapped_type temp_type;
  */    
}

【问题讨论】:

  • 不管错误信息有多复杂,你肯定明白FindTypeForMapping 没有接受OptionsList2 的特化,只有OptionsList。这些是不同的模板!
  • 好的,谢谢。我添加了FindTypeMapping 的副本,接受了OptionsList2。仍然是一个错误,但略有不同。 /* 错误:无效使用不完整类型'struct FindTypeForMapping
  • IMO 的做法是错误的。与其复制东西,不如专注于摆脱宏。添加一个MakeOptionList&lt;Ts...&gt; 元函数,将OptionsList 公开为其::type。然后你就可以摆脱宏了。
  • 好的,比如:template&lt;typename ...Ts&gt; struct MakeOptionsList { typedef OptionsList&lt;Ts...&gt; type; };?
  • 不完全。它应该是递归的,就像宏定义一样。你可以解决的。

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


【解决方案1】:

对不起,但是...您为什么不简单地使用 std::tuple 而不是可变参数 OptionList2

您的FindTypeForMapping 类型特征可以简单地写成(对不起,如果我在FTFM 中缩短了名称)

template <typename, int>
struct FTFM;

template <int n, int no, typename TypeO, typename ... Ts>
struct FTFM<std::tuple<Option<no, TypeO>, Ts...>, n>
 { using type = typename FTFM<std::tuple<Ts...>, n>::type; };

template <int n, typename TypeO, typename ... Ts>
struct FTFM<std::tuple<Option<n, TypeO>, Ts...>, n>
 { using type = TypeO; };

template <int n>
struct FTFM<std::tuple<>, n>
 { using type = NullType; };

以下是一个完整的工作(嗯...编译)示例

#include <tuple>
#include <type_traits>

struct NullType
 { };

template <int n, typename T>
struct Option : public std::integral_constant<int, n>
 { using type = T; };

template <typename, int>
struct FTFM;

template <int n, int no, typename TypeO, typename ... Ts>
struct FTFM<std::tuple<Option<no, TypeO>, Ts...>, n>
 { using type = typename FTFM<std::tuple<Ts...>, n>::type; };

template <int n, typename TypeO, typename ... Ts>
struct FTFM<std::tuple<Option<n, TypeO>, Ts...>, n>
 { using type = TypeO; };

template <int n>
struct FTFM<std::tuple<>, n>
 { using type = NullType; };

template <typename T, int I>
using FTFM_t = typename FTFM<T, I>::type;

int main ()
 {
   using  opt0 = Option<0, void>;
   using  opt1 = Option<1, char>;
   using  opt2 = Option<2, short>;
   using  opt3 = Option<3, int>;
   using  opt4 = Option<4, long>;
   using  opt5 = Option<5, long long>;

   using  optList = std::tuple<opt0, opt1, opt2, opt3, opt4, opt5>;

   static_assert ( std::is_same<void,      FTFM_t<optList, 0>>{}, "!" );
   static_assert ( std::is_same<char,      FTFM_t<optList, 1>>{}, "!" );
   static_assert ( std::is_same<short,     FTFM_t<optList, 2>>{}, "!" );
   static_assert ( std::is_same<int,       FTFM_t<optList, 3>>{}, "!" );
   static_assert ( std::is_same<long,      FTFM_t<optList, 4>>{}, "!" );
   static_assert ( std::is_same<long long, FTFM_t<optList, 5>>{}, "!" );
   static_assert ( std::is_same<NullType,  FTFM_t<optList, 6>>{}, "!" );
 }

【讨论】:

  • 谢谢。不错的解决方案,它删除了 OptionsList 和 CheckedMappedInt。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-17
  • 1970-01-01
  • 2011-06-09
相关资源
最近更新 更多