【问题标题】:How do I sort two tuples of containers?如何对两个容器元组进行排序?
【发布时间】:2016-06-22 15:35:48
【问题描述】:

我有两个元组,每个元组都包含不同类型的容器。

std::tuple<containerA<typesA>...> tupleA;
std::tuple<containerB<typesB>...> tupleB;

所以,作为一个例子,tupleA 可以这样定义:

std::tuple<list<int>, list<float>> tupleA;

containerAcontainerB 这两个容器是不同的类型。 typesAtypesB 不相交。我想按大小对元组内的容器进行排序,并能够按它们的类型访问它们。所以,举个例子

std::tuple<list<int>, list<float>> tupleA {{2}, {3.3f, 4.2f}};
std::tuple<deque<double>, deque<uint8_t>> tupleB {{2.0, 1.2, 4.4}, {}};

auto sortedArray = sort(tupleA, tupleB);
sortedArray == {deque<uint8_t>, list<int>, list<float>, deque<double>};
sortedArray.get<list<float>>() == {3.3f, 4.2f};
std::get<list<int>>(tupleA).push_back(4);
std::get<list<int>>(tupleA).push_back(5);
std::get<list<int>>(tupleA).push_back(6);
sortedArray = sort(tupleA, tupleB);
sortedArray == {deque<uint8_t>, list<float>, deque<double>, list<int>};

最重要的部分是我必须存储sortedArray 并且元组中元素的大小可能会改变。我无法创建这样的sort 函数。访问sortedArray 的实际语法并不重要。

我尝试对sortedArray中的容器数据使用朴素索引,例如使用std::pair&lt;A, 1&gt;来引用元组A中的第二个元素,但是我无法通过它访问元组的信息,因为它不是 constexpr

【问题讨论】:

  • @ArchbishopOfBanterbury 你不能 std::sort 一个元组,因为它们的顺序是不可变的。这样做会创建一个新类型。
  • 您说“我想按大小对元组中的容器进行排序。” “尺寸”是什么意思?它们包含的元素数量?如果是这样,那就是运行时信息,tuple 中的类型排序是类型信息,因此在编译时需要。
  • @JonathanMee 是的,根据它们包含的元素数量。我不是指实际tuples 的排序,我指的是我创建的sortedArray 的排序。这个sortedArray 可以是vector 或您需要的任何类型。
  • 我很好奇你是如何达到这个要求的。
  • @LightnessRacesinOrbit 我同意。毫无疑问,有人会建议使用void* 或其他一些规避语言规则的方法来处理这个问题。但是这个问题与语言本身作斗争。最好的解决方案是利用模板的力量和使用语言的力量。但要找到这样的解决方案,需要更多关于需求动机的信息。

标签: c++ c++11


【解决方案1】:

这是可能的,但并不容易。

首先,您需要生成 N 个元素的每个排列的编译时列表(其中有 N 个阶乘)。

同时编写运行时和编译时置换对象(单独)。

制作从排列到索引的运行时映射。 (地图步骤)

将您的元组转换为 (index, size) 的向量。按大小排序。提取排列。将此映射到排列集的索引。 (排序步骤,使用地图步骤)

编写一个“魔术开关”,它接受一个函数对象f 和一个排列索引,并使用编译时排列调用f。 (魔法步骤)

编写采用编译时间排列并基于它重新排序元组的代码。 (重新排序步骤)

编写接受函数对象ftuple 的代码。做(排序步骤)。执行(魔术步骤),为其提供第二个函数对象g,该对象接受传入的排列和元组和(重新排序步骤),然后用它调用f

调用执行此操作的函数bob

std::tuple<list<int>, list<float>> tupleA {{2}, {3.3f, 4.2f}};
std::tuple<deque<double>, deque<uint8_t>> tupleB {{2.0, 1.2, 4.4}, {}};

bob(concat_tuple_tie(tupleA, tupleB), [&](auto&& sorted_array){
  assert( std::is_same<
    std::tuple<deque<uint8_t>&, list<int>&, list<float>&, deque<double>&>,
    std::decay_t<decltype(sorted_array)>
  >::value, "wrong order" );
  sortedArray.get<list<float>>() == {3.3f, 4.2f};
});
std::get<list<int>>(tupleA).push_back(4);
std::get<list<int>>(tupleA).push_back(5);
std::get<list<int>>(tupleA).push_back(6);
bob(concat_tuple_tie(tupleA, tupleB), [&](auto&& sorted_array){
  assert( std::is_same<
    std::tuple<deque<uint8_t>&, list<float>&, deque<double>&, list<int>&>,
    std::decay_t<decltype(sorted_array)>
  >::value, "wrong order" );
});

就个人而言,我怀疑您是否需要这样做。

我可以做到这一点,但可能需要几个小时,所以我不会为了一个 SO 答案而这样做。您可以查看我的magic switch 代码,了解上述最神奇的内容。另一个困难的部分是排列步骤。

请注意,代码使用延续传递样式。另请注意,lambda 的 每个 排列都被实例化,包括错误的排列,因此您的代码必须对每个排列都有效。这可能会导致大量代码膨胀。

另一种解决方案可能涉及在您的容器周围创建类型擦除包装器并简单地对它们进行排序,但这不是您所要求的。

【讨论】:

    【解决方案2】:

    描述的问题听起来很像你想要动态多态,而不是静态多态——你解决它的困难是因为你'被困在试图用做静态多态的工具来表达。

    即您需要一个集合(或指向集合的指针)的类型(层次结构),让您在运行时选择集合类型和数据类型,但仍为您需要的函数提供统一接口,例如 size(例如,通过继承和虚函数)。

    例如草图的开头可能看起来像

    struct SequencePointer
    {
        virtual size_t size() const = 0;
        virtual boost::any operator[](size_t n) const = 0;
    };
    
    template <typename Sequence>
    struct SLSequencePointer
    {
        const Sequence *ptr;
        size_t size() const { return ptr->size(); }
        boost::any operator[](size_t n) const { return (*ptr)[n]; }
    };
    

    【讨论】:

      猜你喜欢
      • 2023-04-05
      • 2021-12-24
      • 2018-09-20
      • 1970-01-01
      • 2014-07-22
      • 2014-08-05
      • 1970-01-01
      • 2016-07-26
      相关资源
      最近更新 更多