gpt4 book ai didi

Pimpl 习语上下文中的 C++17 自定义迭代器

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:41:52 25 4
gpt4 key购买 nike

我想知道如何在不公开实现的情况下使 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;
};

iteratorconst_iterator 基本上是内部容器迭代器的类型定义。这就是问题所在。

首先,我不想在 StreamIterator header 中公开私有(private)实现。因此,此处不允许使用 iteratorconst_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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com