仅排序功能不同的已排序容器的 C++ 抽象?
A C++ abstraction for sorted containers that differ only in sort function?
在 C++ 中,我有一个 class,其中包含两个成员,它们是已排序的容器。容器存储相同的对象类型,但一个按升序排序,另一个按降序排序。注意:每个容器中的对象type是相同的,但是两个容器中实际存储的对象instances是不同的。也就是说,数据没有重复。
我有几个访问容器中项目的模板方法,模板参数说明要访问哪个容器(即按升序排序的容器,或另一个)。
无论我迭代哪个容器,我都执行完全相同的操作。但是我访问项目的顺序很重要。
所以我正在寻找一种抽象或语言结构,使我能够以通用方式使用这两个容器。
这里有一个例子可以帮助解释我正在尝试做的事情:
#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::迭代器。并调用 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 模板,并使用 if constexpr 或标签调度。您还可以使 lambda 成为一个单独的成员函数
在 C++ 中,我有一个 class,其中包含两个成员,它们是已排序的容器。容器存储相同的对象类型,但一个按升序排序,另一个按降序排序。注意:每个容器中的对象type是相同的,但是两个容器中实际存储的对象instances是不同的。也就是说,数据没有重复。
我有几个访问容器中项目的模板方法,模板参数说明要访问哪个容器(即按升序排序的容器,或另一个)。
无论我迭代哪个容器,我都执行完全相同的操作。但是我访问项目的顺序很重要。
所以我正在寻找一种抽象或语言结构,使我能够以通用方式使用这两个容器。
这里有一个例子可以帮助解释我正在尝试做的事情:
#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::迭代器。并调用 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 模板,并使用 if constexpr 或标签调度。您还可以使 lambda 成为一个单独的成员函数