c++迭代不同类型的多个有序集合

62o28rlo  于 2023-02-06  发布在  其他
关注(0)|答案(1)|浏览(113)

标准库中是否有支持以下用例的部分:您有N个集合,每个集合可能是一种集合类型(C1、C2 ...、Cn),所有集合都支持begin()end()和迭代,即向量、双端队列等。
这些集合中的每一个都可以包含不同类型的对象,即,集合是C1、C2、C3,并且具有不同的大小。
此外,所有这些类型都可以通过时间戳排序,但是每个项存储时间戳的方式不同。例如,类型A有成员A.timestamp,B有成员B.TimeStamp,C有函数C.GetTimestamp()
每个集合都已通过此函数排序。
我想要做的是使用order函数按顺序迭代所有集合中的所有项,并调用另一个函数,即访问函数,std::function<void(A &)>用于A的集合,std::function<void(B&)>用于类型B的项,等等。
然后,我想按时间戳顺序调用每个项目。例如:

class A
{
   public:
      time_t timeStamp;
      int  length;
};

class B
{
    public:
    B(time_t _tm, std::string _name):timestamp(_tm), name(_name){}
    time_t GetTimestamp() { return timeStamp; }
    std::string GetName() { return name; }
private:
    time_t timeStamp;
    std::string name;
}

std::vector<A> listA {1, 4}, {5, 7}, {8,9});

std::deque<B>  listB { B(0,"bob"), B(3, "Frank") };

// iterate over listA and listB in time sequential order
// for items in A call [](const A&a) { std::cout << a.length << std::endl; }
// for items in B call [](const B&b) { std::cout << b.name << std::endl; }
// Output would be:
// bob
// 4
// Frank
// 7
// 9

我对实现的想法是,定义一个基类,该基类由排序函数返回的类型模板化:

template<classname O>
class VisitedCollection
{
   public:
      virtual bool end() = 0; // returns if we are at the end of collection
      virtual O next_order_measure() = 0; returns an instance of a class that can be used for ordering
      virtual void visit();
};

class VisitedCollectionA: VisitedCollection<time_t>
{
    public:
       VisitedCollectionA(std::vector<A> &&a): items(std::move(a))
       {
           next_item = items.begin();
       }
       virtual bool end() override { return next_item == items.end(); }
       virtual time_t next_order_meaure() override { return nextItem->timeStamp;}
       virtual void visit() override { std::cout << nextItem->length << std::endl;}

    private:

        std::vector<A>            &items;
        std::vector<A>::iterator  next_item;

        
}
... similar for class B
... could also add a class C, D, etc

Now I can create a collection of VisitedCollection<time_t>, and add VisitedCollectionA, and VisitedCollectionB. This collection of collections would:

首先查看每个集合中第一个项目的排序函数的返回值。哪个项目的值最小,调用它的访问者函数。然后找到下一个项目的排序值最小的集合。在排序函数的约束下,迭代集合的“集合”中第一个出现的集合。一旦集合到达“end”,它就从迭代中删除。
我正在考虑自己的,但想知道在标准库中是否已经有类似的东西,我甚至可以使visit()成为lambda,这将允许VisitedCollectionA类型成为模板,它接受排序类型和集合类型,这将允许主访问者的创建用类似于

{
  VisitedCollection<time_t, std::vector<A>>
  (
   vecA, 
   [](){ return next_item->timeStamp; }, 
   [](const A&a) { std::cout << a << std::endl; }
  ),
  VisitedCollection<time_t, std::deque<B>>
  (
   deqB, 
   [](){ return next_item->GetTimestamp(); }, 
   [](const B&b) { std::cout << a << std::endl; }
  )
}

这感觉有点像变量和范围的混合
有这样的东西吗?

gjmwrych

gjmwrych1#

在C++ std中没有直接的解决方案(或者其他的std,我猜?),但是不管怎样,在那里可以找到一些对自制解决方案的支持。尽管不是最优的,但是如果需要的话,它应该很容易理解和重写。
首先,你需要const getter(这里省略了)和一个统一的接口来从迭代的类型中获取时间戳:

time_t timestamp(A const& a) { return a.timeStamp; }
time_t timestamp(B const& b) { return b.GetTimestamp(); }
template<typename... Ts> time_t timestamp(std::variant<Ts...> const& v) {
    return visit([](auto&& e) { return timestamp(e); }, v);
} // see below

基本思想是将所有元素的引用放入一个容器中(作为变量),排序然后访问它们:

void visit_sorted_timestamps(auto visitor, auto&&... ranges) {
    std::vector<std::variant<
            std::reference_wrapper<std::ranges::range_value_t<decltype(ranges)>>...
    >> mixed;
    mixed.reserve((... + size(ranges)));
    (..., mixed.insert(end(mixed), begin(ranges), end(ranges)));
    std::sort(begin(mixed), end(mixed), [](auto&& v1, auto&& v2) {
        return timestamp(v1) < timestamp(v2);
    });
    for (auto&& v: mixed) visit(visitor, v);
}

用法示例:

int main() {
    visit_sorted_timestamps(Overload{
        [](A const& a) { std::cout << a.length << '\n'; },
        [](B const& b) { std::cout << b.GetName() << '\n'; }
    }, listA, listB);
}

如果你还没有lambda "重载"结构体,那么就这样做:

template<typename... Fs> struct Overload: Fs... { using Fs::operator()...; };
template<typename... Fs> Overload(Fs...) -> Overload<Fs...>;

相关问题