如何在自定义 class (w/o boost) 中隐藏迭代器和容器实现
How to hide Iterator and container implemtation in a custom class (w/o boost)
最近一天左右,我一直在为这件事绞尽脑汁。我试图找出如何从 class return 迭代器,同时隐藏 class 正在使用的容器类型。一个例子是我有一个 class canvas,它包含具有相同界面的小部件,并且它们被私密地存储在 std::vector 中。所以....
简化代码
class Canvas
{
public:
WidgetIterator begin();
WidgetIterator end();
private:
class Impl;
std::unique_ptr<Impl> mImpl;
};
class Canvas::Impl
{
public:
std::vector<widget> mWidget;
//how to return mWidget.begin(), mWidget.end() up as widget iterator??
}
usage:
int main()
{
Canvas canvas();
...Fill canvas with widgets...
//iterate over widgets
for(auto& a_widget : canvas)
{
//do something with / to a_widget. User does not know or care
//that underlying iterator is std::vector<widget>::iterator
}
...Do more stuff....
return 0;
}
基本上,我想通过 Canvas::begin() 和 Canvas::end() 以某种方式为 mWidget.begin() 和 mWidget.end() 添加别名。用户知道迭代器是一个小部件,他们只是不需要知道迭代器是 std::vector::iterator。我正在尝试使用 PIMPL 隐藏我的实现并保留有关内容如何存储在 class.
中的信息
我好像找不到合适的"formula"。我查看了 type-erasure 并尝试通过接口 return 函数指针,但我似乎想不出一种方法将 std::vector::iterator 排除在 header,到目前为止,我所看到的一切似乎都不符合我想要做的事情。有人能指出我正确的方向吗?阅读material?我缺少一个概念吗?哦 - 我已经看到一些使用 boost int 的例子,我无法弄清楚如何在我的情况下工作。我想避免这种情况,因为我正在尝试减少外部依赖性。
键入擦除迭代器的最简单方法是编写输入迭代器生成器。
这足以 for(:)
循环,但不是所有其他算法,并且可以轻松包装任何随机访问容器:
template<class T>
struct gen_iterator_t {
std::function<T(std::size_t)> f;
std::size_t n = 0;
gen_iterator& operator++() { ++n; return *this; }
gen_iterator operator++(int) { auto r = *this; ++*this; return r; }
T operator*() { return f(n); }
gen_iterator_t( gen_iterator_t const& )=default;
gen_iterator_t( gen_iterator_t && )=default;
gen_iterator_t& operator=( gen_iterator_t const& )=default;
gen_iterator_t& operator=( gen_iterator_t && )=default;
gen_iterator_t()=default;
explicit operator bool() const { return static_cast<bool>(f); }
gen_iterator_t( std::function<T(std::size_t)> fin, std::size_t i ):
f(fin), n(i)
{}
friend bool operator==( gen_iterator_t const& lhs, gen_iterator_t const& rhs ) {
if (!lhs && !rhs) return true;
if (!lhs || !rhs) return false;
return lhs.n == rhs.n;
}
friend bool operator==( gen_iterator_t const& lhs, gen_iterator_t const& rhs ) {
return !(lhs==rhs);
}
};
然后我们写range
:
template<class It>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
};
template<class It>
range_t<It> range( It s, It f ) { return {s,f}; }
并生成范围:
template<class F>
range_t< gen_iterator_t< std::result_of_t<F&(std::size_t)> > >
generate_range( F f, std::size_t start, std::size_t finish ) {
return { {f, start}, {f, finish} };
}
现在你的class暴露了
class Canvas {
public:
range_t< gen_iterator_t< widget& > > get_widgets();
};
实现为
range_t< gen_iterator_t< widget& > > Canvas::get_widgets() {
return generate_range(
[this]( std::size_t n )->widget& { return mImpl->mWidget[n]; },
0, mImpl->mWidget.size()
);
}
很少暴露。
如果您想更进一步,让它能够包装非随机访问容器,那就需要多做一些工作了。
最近一天左右,我一直在为这件事绞尽脑汁。我试图找出如何从 class return 迭代器,同时隐藏 class 正在使用的容器类型。一个例子是我有一个 class canvas,它包含具有相同界面的小部件,并且它们被私密地存储在 std::vector 中。所以....
简化代码
class Canvas
{
public:
WidgetIterator begin();
WidgetIterator end();
private:
class Impl;
std::unique_ptr<Impl> mImpl;
};
class Canvas::Impl
{
public:
std::vector<widget> mWidget;
//how to return mWidget.begin(), mWidget.end() up as widget iterator??
}
usage:
int main()
{
Canvas canvas();
...Fill canvas with widgets...
//iterate over widgets
for(auto& a_widget : canvas)
{
//do something with / to a_widget. User does not know or care
//that underlying iterator is std::vector<widget>::iterator
}
...Do more stuff....
return 0;
}
基本上,我想通过 Canvas::begin() 和 Canvas::end() 以某种方式为 mWidget.begin() 和 mWidget.end() 添加别名。用户知道迭代器是一个小部件,他们只是不需要知道迭代器是 std::vector::iterator。我正在尝试使用 PIMPL 隐藏我的实现并保留有关内容如何存储在 class.
中的信息我好像找不到合适的"formula"。我查看了 type-erasure 并尝试通过接口 return 函数指针,但我似乎想不出一种方法将 std::vector::iterator 排除在 header,到目前为止,我所看到的一切似乎都不符合我想要做的事情。有人能指出我正确的方向吗?阅读material?我缺少一个概念吗?哦 - 我已经看到一些使用 boost int 的例子,我无法弄清楚如何在我的情况下工作。我想避免这种情况,因为我正在尝试减少外部依赖性。
键入擦除迭代器的最简单方法是编写输入迭代器生成器。
这足以 for(:)
循环,但不是所有其他算法,并且可以轻松包装任何随机访问容器:
template<class T>
struct gen_iterator_t {
std::function<T(std::size_t)> f;
std::size_t n = 0;
gen_iterator& operator++() { ++n; return *this; }
gen_iterator operator++(int) { auto r = *this; ++*this; return r; }
T operator*() { return f(n); }
gen_iterator_t( gen_iterator_t const& )=default;
gen_iterator_t( gen_iterator_t && )=default;
gen_iterator_t& operator=( gen_iterator_t const& )=default;
gen_iterator_t& operator=( gen_iterator_t && )=default;
gen_iterator_t()=default;
explicit operator bool() const { return static_cast<bool>(f); }
gen_iterator_t( std::function<T(std::size_t)> fin, std::size_t i ):
f(fin), n(i)
{}
friend bool operator==( gen_iterator_t const& lhs, gen_iterator_t const& rhs ) {
if (!lhs && !rhs) return true;
if (!lhs || !rhs) return false;
return lhs.n == rhs.n;
}
friend bool operator==( gen_iterator_t const& lhs, gen_iterator_t const& rhs ) {
return !(lhs==rhs);
}
};
然后我们写range
:
template<class It>
struct range_t {
It b, e;
It begin() const { return b; }
It end() const { return e; }
};
template<class It>
range_t<It> range( It s, It f ) { return {s,f}; }
并生成范围:
template<class F>
range_t< gen_iterator_t< std::result_of_t<F&(std::size_t)> > >
generate_range( F f, std::size_t start, std::size_t finish ) {
return { {f, start}, {f, finish} };
}
现在你的class暴露了
class Canvas {
public:
range_t< gen_iterator_t< widget& > > get_widgets();
};
实现为
range_t< gen_iterator_t< widget& > > Canvas::get_widgets() {
return generate_range(
[this]( std::size_t n )->widget& { return mImpl->mWidget[n]; },
0, mImpl->mWidget.size()
);
}
很少暴露。
如果您想更进一步,让它能够包装非随机访问容器,那就需要多做一些工作了。