问题描述
在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设置为单独的成员函数