- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我想知道如何在不公开实现的情况下使 C++ 类可迭代(STL 兼容)?
项目结构如下:
流
class Stream
{
public:
Stream();
[...]
StreamIterator iter()
{
return StreamIterator(this);
}
private:
class impl;
std::unique_ptr<impl> pimpl;
};
流过滤器
class StreamFilter
{
public:
StreamFilter();
[...]
private:
class impl;
std::shared_ptr<impl> pimpl;
};
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
StreamFilter
是不同过滤策略的基类。
在示例代码中为了简单起见,我使用了原始内存指针,当然在真正的开发代码中我使用了智能指针:共享、弱...
我想让 StreamIterator
以 STL 方式变得可迭代,这样做:
StreamIterator iter = stream.iter();
iter.addFilter(new FilterByOffset([...with parameters...]));
for (auto item : iter)
{
[...doing something with filtered items...]
}
基本方法是添加一些访问器以允许基于范围的 for 循环。
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
iterator begin();
iterator end();
const_iterator cbegin() const;
const_iterator cend() const;
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
iterator
和 const_iterator
基本上是内部容器迭代器的类型定义。这就是问题所在。
首先,我不想在 StreamIterator
header 中公开私有(private)实现。因此,此处不允许使用 iterator
和 const_iterator
。
其次,由于流过滤策略,返回的迭代器不仅仅是一些内部STL容器的别名。在内部实现中,我需要以仿函数的方式调用过滤器来检查是否需要排除该项目。
StreamIterator
header 中唯一允许的类型是返回的项目对象的类型。
有办法吗?
非常感谢!
附加信息:
也许这个声明是一种允许私有(private)实现的方式,我需要进行更多调查:
StreamIterator
class StreamIterator
{
public:
StreamIterator(Stream* streamToFilter);
[...]
struct iterator
{
Object::Ptr operator*();
iterator& operator++();
bool operator!= (const iterator& it) const;
};
typedef typename StreamIterator::iterator iterator;
iterator begin();
iterator end();
[...]
void addFilter(StreamFilter* filter);
void removeFilter(StreamFilter* filter);
[...]
private:
class impl;
std::unique_ptr<impl> pimpl;
};
最佳答案
首先,不要把它叫做StreamIterator
;迭代器是一个类似指针的对象,其中 2 个可以指定一个范围。您的 StreamIterator
没有这个。在这里重用定义明确的迭代器术语不会有任何好处。
现在,您的StreamIterator
是某种范围 的迭代器。所以我们称它为 StreamRange
。
为了隐藏 StreamRange
的迭代方式,您必须隐藏它使用的迭代器的工作方式。
而这——隐藏流迭代器的实现细节——会带来巨大的成本。
当遍历循环时,循环中的每个步骤都涉及++
和*
以及一个==
。在其中每一个上抛出一个指针间接和一个 vtable 查找(或等效的)将使你的循环慢得多。
但这是如何做到的。
template<class Value>
struct any_input_iterator {
using difference_type = std::ptrdiff_t;
using value_type=Value;
using pointer = value_type*;
using reference = value_type;
using iterator_category = std::input_iterator_tag;
private:
struct vtable_t {
bool(*equal)( std::any const& lhs, std::any const& rhs ) = 0;
Value(*get)( std::any& ) = 0;
void(*inc)( std::any& ) = 0;
};
vtable_t const* vtable = 0;
std::any state;
template<class U>
static vtable_t make_vtable() {
return {
[](std::any const& lhs, std::any const& rhs)->bool {
return std::any_cast<U const&>(lhs) == std::any_cast<U const&>(rhs);
},
[](std::any& src)->Value {
return *std::any_cast<U&>(src);
},
[](std::any& src) {
++std::any_cast<U&>(src);
}
};
}
template<class U>
static vtable_t const* get_vtable() {
static const auto vtable = make_vtable<U>();
return &vtable;
}
public:
template<class U,
std::enable_if_t<!std::is_same<std::decay_t<U>, any_input_iterator>{}, bool> = true
>
any_input_iterator(U&& u):
vtable(get_vtable<std::decay_t<U>>()),
state(std::forward<U>(u))
{}
any_input_iterator& operator++() { vtable->inc(state); return *this; }
any_input_iterator operator++(int) { auto tmp = *this; ++*this; return tmp; }
reference operator*() { return vtable->get(state); }
friend bool operator==( any_input_iterator const& lhs, any_input_iterator const& rhs ) {
if (lhs.vtable != rhs.vtable) return false;
if (!lhs.vtable) return true;
return lhs.vtable->equal( lhs.state, rhs.state );
}
friend bool operator!=( any_input_iterator const& lhs, any_input_iterator const& rhs ) {
return !(lhs==rhs);
}
struct fake_ptr {
Value t;
Value* operator->()&&{ return std::addressof(t); }
};
fake_ptr operator->()const { return {**this}; }
};
可能有一些错别字,但这是基本的类型删除。 Boost 在这方面做得更好。
我只支持输入迭代器。如果你想支持前向迭代器,你必须改变一些类型定义和返回引用等。
any_input_iterator<int> begin();
any_input_iterator<int> end();
但是,这足以让其他人遍历您的相关范围。
它会很慢,但它会起作用。
关于Pimpl 习语上下文中的 C++17 自定义迭代器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48301900/
场景如下: 我将我的应用程序运行所需的几个 .xml(某种配置)文件捆绑在一个 .jar 文件中。 jar 文件具有以下结构: settings-1.0.0.jar ˪ resources/ ˪
我是一名优秀的程序员,十分优秀!