【问题标题】:How to create a sorted mapping integer index with templates如何使用模板创建排序映射整数索引
【发布时间】:2016-04-26 12:50:58
【问题描述】:

我有数据结构:

template <int...I> struct index {};

template <typename...T> struct data {};

template <int I, int J> struct X
{
   static constexpr int i = I;
   static constexpr int j = J;
};

typedef data< X<0,4>, X<1,2>, X<2,1>, X<1,6>, X<1,3> > data_t;

其中 data 不包含重复项且索引 J 很小,范围为 0-31。

我想创建一个静态索引,其中包含所有 X 在 data 中的位置,索引 I 等于某个给定值(例如 I=1),按索引 J 排序。它是“排序”,我觉得很难。

例如,我想实现一个类 build_index,这样:

typedef build_index<1,data>::type_t index_t;

生成相同的:

typedef index<1, 4, 3> index_t;

反映了由 J 排序的元素 X(1,J) 在 data 中的位置: X(1,2) 在数据(1),X(1,3) 在数据(4),X(1,6) 在数据(3)

我不想使用 STL,因为它不适用于 gcc-avr,尽管我可以移植选定的 sn-ps。

【问题讨论】:

  • 您可以更改数据吗?
  • 您是否考虑过 Boost 元编程库 (boost.mpl)。例如:boost.org/doc/libs/1_60_0/libs/mpl/doc/refmanual/sort.html您可以滚动自己的排序,但使用其他人已经编写的排序会更容易。
  • 您的问题被标记为 C++11。如果能用C++14,那就简单多了。
  • @5gon12eder:我可以使用 C++14
  • @fish2000 在 C++14 中,您可以简单地将信息存储在 constexpr 数组中,并使用普通 C++ 代码进行搜索和排序。由于标准库还没有相关的constexpr 函数,因此您必须自己编写它们,这就是为什么它仍然不是一项简单的任务,这就是为什么我还没有写答案的原因。

标签: c++ sorting c++11 metaprogramming c++14


【解决方案1】:

我将分享我对这个问题的处理方法,我认为这很巧妙。我用过C++11std::conditional,C++14std::integer_sequencestd::make_integer_sequence,网上都可以找到实现。

让我们从您拥有的数据结构开始。

template <int... Is> struct index {};
template <typename... Ts> struct list {};
template <int L, int R> struct pair {};

我们将使用元函数concat,它连接N 类型列表。我们使用它来过滤列表,当谓词返回true 时返回list&lt;T&gt;,否则返回list&lt;&gt;

例如,要过滤list&lt;1, 3, 2, 4, 2&gt;中的偶数,我们可以对每个I执行std::conditional_t&lt;I % 2 == 0, list&lt;I&gt;, list&lt;&gt;&gt;,得到concat_t&lt;list&lt;&gt;, list&lt;&gt;, list&lt;2&gt;, list&lt;4&gt;, list&lt;2&gt;&gt; = list&lt;2, 4, 2&gt;

template <typename... Ts> struct concat;

template <> struct concat<> { using type = list<>; }

template <typename... Ts>
struct concat<list<Ts...>> { using type = list<Ts...>; };

template <typename... Ts, typename... Us>
struct concat<list<Ts...>, list<Us...>> { using type = list<Ts..., Us...>; };

template <typename... Ts, typename... Us, typename... Tail>
struct concat<list<Ts...>, list<Us...>, Tail...>
    : concat<list<Ts..., Us...>, Tail...> {};

template <typename... Ts>
using concat_t = typename concat<Ts...>::type;

现在我们到达build_index。我们在[0, 32) 的已知范围内执行桶排序。我们本可以使用通用排序算法,但作弊更有趣。

template <int N, typename T> struct build_index;

// e.g., `build_index<
//            1, list<pair<0, 4>, pair<1, 2>, pair<2, 1>, pair<1, 6>, pair<1, 3>>`
template <int N, int... Ls, int... Rs>
struct build_index<N, list<pair<Ls, Rs>...>> {
  // Filter for pairs where `N == lhs`, and replace the `lhs` with the index.
  template <int... Is>
  static auto filter(std::integer_sequence<int, Is...>)
      -> concat_t<std::conditional_t<N == Ls, list<pair<Is, Rs>>, list<>>...>;

  // e.g., `list<pair<1, 2>, pair<3, 6>, pair<4, 3>>`
  using filtered =
      decltype(filter(std::make_integer_sequence<int, sizeof...(Ls)>{}));

  // `get<I>(set)` returns the `lhs` if `set` can implicitly convert to
  // `pair<lhs, I>` for some `lhs`, and nothing otherwise.
  template <typename... Ts> struct set : Ts... {};
  template <int I, int L> static list<index<L>> get(pair<L, I>);
  template <int I> static list<> get(...);

  // We invoke `get<I>` for `I` in `[0, 32)` to sort `filtered`.
  template <int... Is, typename... Ts>
  static auto sort(std::integer_sequence<int, Is...>, list<Ts...>)
      -> concat_t<decltype(get<Is>(set<Ts...>{}))...>;

  // e.g., `list<index<1>, index<4>, index<3>>`
  using sorted =
      decltype(sort(std::make_integer_sequence<int, 32>{}, filtered{}));

  // e.g., `list<1, 4, 3>`
  template <int... Is> static index<Is...> indices(list<index<Is>...>);
  using type = decltype(indices(sorted{}));
};

template <int N, typename... Ts>
using build_index_t = typename build_index<N, Ts...>::type;

我们得到:

using index_t = build_index_t<
    1, list<pair<0, 4>, pair<1, 2>, pair<2, 1>, pair<1, 6>, pair<1, 3>>>;
static_assert(std::is_same<index<1, 4, 3>, index_t>::value, "");

【讨论】:

    【解决方案2】:

    当您在 C++ 模板编程中遇到复杂的问题时,尝试将其分解为几个较小的步骤(就像大多数编程问题一样)通常会有所帮助。这是一个可能的路径:

    1. 选择与所选 I 匹配的 X 并将它们存储在新的数据类型中,将 I 替换为位置(为此使用递归)
    2. 将 selected_data 中的 X 按 J 排序。我觉得这样写有点烦人。也许您应该为此创建一个单独的问题。
    3. 从排序和选择的 X 中提取位置

    这是相应的代码。我正在使用std::conditional,但它当然很容易替换。我在测试中使用std::is_same,当然你并不需要它(否则实现起来很简单)。

    std::conditional 和 std::is_same 的东西 + 实用程序头

    #include <utility>
    
    template <int... I>
    struct index
    {
    };
    
    template <typename... T>
    struct data
    {
    };
    
    template <int I, int J>
    struct X
    {
      static constexpr int i = I;
      static constexpr int j = J;
    };
    
    typedef data<X<0, 4>, X<1, 2>, X<2, 1>, X<1, 6>, X<1, 3>> data_t;
    

    提取与我们要查找的I 匹配的Xs,并将is 替换为该位置。

    template <int Pos, int I, typename Extracted, typename Rest>
    struct ExtractImpl;
    
    template <int Pos, int I, typename... ExtractedX>
    struct ExtractImpl<Pos, I, data<ExtractedX...>, data<>>
    {
      using type = data<ExtractedX...>;
    };
    
    template <int Pos, int I, typename... ExtractedX, typename T, typename... Rest>
    struct ExtractImpl<Pos, I, data<ExtractedX...>, data<T, Rest...>>
    {
      using type = typename std::conditional<
          (T::i == I),
          typename ExtractImpl<Pos + 1,
                               I,
                               data<ExtractedX..., X<Pos, T::j>>,
                               data<Rest...>>::type,
          typename ExtractImpl<Pos + 1, I, data<ExtractedX...>, data<Rest...>>::
              type>::type;
    };
    
    template <int I, typename Data>
    struct Extract
    {
      using type = typename ExtractImpl<0, I, data<>, Data>::type;
    };
    
    using extracted = typename Extract<1, data_t>::type;
    static_assert(std::is_same<extracted, data<X<1, 2>, X<3, 6>, X<4, 3>>>::value, "");
    

    按 J 排序。这是通过将元素增量插入排序列表来完成的。可能有更优雅的方式来做到这一点。

    template <typename T, typename LessList, typename RestList>
    struct insert_impl;
    
    template <typename T, typename... Lesser>
    struct insert_impl<T, data<Lesser...>, data<>>
    {
      using type = data<Lesser..., T>;
    };
    
    template <typename T, typename... Lesser, typename Next, typename... Rest>
    struct insert_impl<T, data<Lesser...>, data<Next, Rest...>>
    {
      using type = typename std::conditional<
          (T::j < Next::j),
          data<Lesser..., T, Next, Rest...>,
          typename insert_impl<T, data<Lesser..., Next>, data<Rest...>>::type>::
          type;
    };
    
    template <typename T, typename SortedList>
    struct insert
    {
      using type = typename insert_impl<T, data<>, SortedList>::type;
    };
    
    template <typename SortedList, typename UnsortedList>
    struct SortImpl;
    
    template <typename SortedList>
    struct SortImpl<SortedList, data<>>
    {
      using type = SortedList;
    };
    
    template <typename SortedList, typename T, typename... UnsortedX>
    struct SortImpl<SortedList, data<T, UnsortedX...>>
    {
      using type = typename SortImpl<typename insert<T, SortedList>::type,
                                     data<UnsortedX...>>::type;
    };
    
    template <typename UnsortedList>
    struct Sort
    {
      using type = typename SortImpl<data<>, UnsortedList>::type;
    };
    
    using sorted = typename Sort<extracted>::type;
    static_assert(std::is_same<sorted, data<X<1, 2>, X<4, 3>, X<3, 6>>>::value, "");
    

    最后,提取你要找的索引:

    template <typename List>
    struct Indexes;
    
    template <typename... Data>
    struct Indexes<data<Data...>>
    {
      using type = index<Data::i...>;
    };
    
    using result = typename Indexes<sorted>::type;
    static_assert(std::is_same<result, index<1, 4, 3>>::value, "");
    

    警告词:虽然我在代码中看不到任何问题,但除了您的示例之外,我还没有对其进行测试...

    【讨论】:

    • 非常好!谢谢。 +1
    • 很高兴你喜欢它 :-) 顺便说一句:当你认为你得到了你正在寻找的答案并且不期望很快会有更好的答案时,你应该将一个答案标记为正确的答案。目前,您的所有问题都列为未回答。
    猜你喜欢
    • 2018-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-23
    相关资源
    最近更新 更多