仅排序功能不同的排序容器的C ++抽象?

问题描述

在C ++中,我有一个包含两个已排序容器成员的类。容器存储相同的对象类型,但其中一个按升序排序,另一个按降序排序。注意:每个容器中的对象 type 是相同的,但是存储在两个容器中的实际对象 instances 是不同的。换句话说,数据不会重复。

我有几种访问容器中项目的模板方法,其中template参数说明要访问哪个容器(即,一个以升序排序,另一个则访问)。

无论我在哪个容器上进行迭代,我都会执行完全相同的操作。但是访问物品的顺序很重要。

因此,我正在寻找一种抽象或语言构造,以允许我以通用方式使用这两个容器。

下面是一个示例,可以帮助您解释我正在尝试做的事情:

#include <set>
#include <iostream>

class MySets
{
    public:
        using fwd_set_t = std::set<int,std::less<int>>;
        using rev_set_t = std::set<int,std::greater<int>>;

        // BEGIN: added in edit
        using MyIterator = fwd_set_t::iterator;
        using MyConstIterator = fwd_set_t::const_iterator;

        // is this safe??? the compiler isn't complaining that I'm using
        // fwd_set_t::iterator and rev_set_t::iterator interchangeably
        template <bool IS_FWD> inline MyConstIterator begin() const
        {
            return (IS_FWD ? fwd_set_.begin() : rev_set_.begin());
        }

        template <bool IS_FWD> inline MyConstIterator end() const
        {
            return (IS_FWD ? fwd_set_.end() : rev_set_.end());
        }

        template <bool IS_FWD> inline void printSetAlternate() const
        {
            for (auto iter = begin<IS_FWD>(); iter != end<IS_FWD>(); ++iter)
            {
                std::cout << "  " << (*iter) << "\n";
            }
        }
        // END: added in edit


        MySets()
            : fwd_set_({10,20,30,40,50}),rev_set_({11,21,33,44,55})
            {
            }

        template <bool IS_FWD> inline void printSet() const
        {
            //auto const& s = (IS_FWD ? fwd_set_ : rev_set_); // ERROR - different types
            //for (auto const& n : s) { std::cout << "  " << n << "\n"; }

            if (IS_FWD) { for (auto const& n : fwd_set_) { std::cout << "  " << n << "\n"; } }
            else        { for (auto const& n : rev_set_) { std::cout << "  " << n << "\n"; } }
        }

    private:
        fwd_set_t fwd_set_;
        rev_set_t rev_set_;
};

int main(int argc,char* argv[])
{
    MySets ms;

    std::cout << "fwd:\n";
    ms.printSet<true>();

    std::cout << "rev:\n";
    ms.printSet<false>();

    return 0;
}

该示例的关键部分是printSet()方法。注释掉的代码给出了编译器错误(“'::'的操作数具有不同的类型”),但显示了我想要执行的操作的意图。想象一下,这个功能并不是微不足道的,但是可以对集合中的每个项目执行相当多的逻辑。我宁愿不像示例中那样复制和粘贴代码

所以我觉得应该有某种抽象或语言功能可以用来实现预期的结果,但是我不确定那是什么。

注意::我被迫使用C ++ 14,因此任何使用较新语言功能解决方案都不适合我。

编辑:我在上面的示例中添加了一些代码。编译器不会抱怨使用 fwd _set_t :: iterator也引用 rev _set_t :: iterator。并调用printSetAlternate()可以正常工作。我的问题是,这样做安全吗?我想将其与下面@NathanOliver的建议结合起来。实际的用例涉及将迭代器传递给不同的函数

解决方法

您可以编写一些私有帮助器功能和标记,例如

struct fwd_tag_t{};
struct rev_tag_t{};
auto& getSet(fwd_tag_t) const { return fwd_set_; }
auto& getSet(rev_tag_t) const { return rev_set_; }

,然后使用标签分发功能调用该标签以获取正确的设置,例如

template <bool IS_FWD> inline void printSet() const
{
    auto const& s = getSet(std::conditional_t<IS_FWD,fwd_tag_t,rev_tag_t>{});
    for (auto const& n : s) { std::cout << n << " "; }
    std::cout << "\n";
}

如果可以升级到C ++ 17,则可以删除标记和重载并将getSet更改为

template <bool IS_FWD> inline auto& getSet() const
{
    if constexpr(IS_FWD) 
        return fwd_set;
    else 
        return rev_set;
}

然后printSet将变成

template <bool IS_FWD> inline void printSet() const
{
    auto const& s = getSet();
    for (auto const& n : s) { std::cout << n << " "; }
    std::cout << "\n";
}
,

根据您要使用的方式,另一种选择是将数据保留一次,然后使用reverse_iterator

void printSet(bool is_fwd)
{
    auto print = [](auto first,auto last) {
       while(first != last) {  std::cout << *first++ << "\n"; }
    };
    if (is_fwd) {
      print(fwd_set_.begin(),fwd_set.end());
    }  else {
      print(fwd_set.rbegin(),fwd_set.rend());
    }
}

随意使printSet成为模板,并在constexpr或tag调度时使用。您还可以将lambda设置为单独的成员函数